Доброе время суток.
Появилась потребность видеть кто где на свичах сидит, а если быть точным, то на каком порту какой MAC/IP/Производитель железа.
Свичей много (целый зоопарк), как и агрегатов на которых они расположены. Ходить к каждому на морду не удобно, тем более свичи знают только MAC адреса.
Каждый агрекат закрыт маршрутизатором Cisco и это огромный плюс т.к кошка имеет ARP таблицу и знает кто есть кто.
- Drawing3.jpg (19.49 КБ) 18736 просмотров
Нам остаётся только спросить у нужного свича таблицу MAC адресов, а у кошки ARP таблицу и свести все это в единую картину. Смотреть на итоговый результат захотелось через Web.
Для реализации задуманного сетевое оборудование должно поддерживать
SNMP и стандарт iso, на web сервере установлен
PHP и к нему модуль
php5-snmp
Как это должно выглядеть (многое замазано, но в целом понятно):
Перейдем к реализации.
На Web сервере создаем каталог (например map, именно он фигурирует в исходниках, исправьте на свой) который должен содержать три файла:
.htaccess
Код: Выделить всё
DirectoryIndex index index.php
DirectorySlash off
Options -Indexes -MultiViews
RewriteEngine On
RewriteBase /map/
RewriteCond %{REQUEST_URI} \.(css|jpg|gif|png|zip|rar|doc|xls|js|tif|tiff|docx|xlsx|ico)$
RewriteRule ^(.*)$ $1 [L,QSA]
RewriteCond %{ENV:NS} !=1
RewriteCond %{IS_SUBREQ} =true
RewriteRule (.*) $1 [L,QSA]
RewriteCond %{REQUEST_URI} ^/index$ [OR]
RewriteCond %{REQUEST_URI} ^/index[.]+(\w+)$
RewriteRule . / [R=301,L]
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} (.*)/$
RewriteRule . %1 [R=301,L,E=NS:1,QSA]
RewriteCond %{REQUEST_URI} ^[\w\-.]+$
RewriteCond %{REQUEST_FILENAME} (.*)\.(html|php)$
RewriteCond %1.php -s [OR]
RewriteCond %1.html -s
RewriteRule . %1.%2 [L,QSA]
RewriteRule (.*) index.php?$1 [L,QSA]
Необходимо изменить RewriteBase /map/ на Ваш путь.
Я не силен в работе с Апачем, поэтому файл писался по различным мануалам. Главная задача .htaccess - создание читаемых ссылок вида:
http://localhost/map/имя_агрегата/номер_свича
Очень много лишнего для будущих модификаций скрипта.
index.php
Код: Выделить всё
<?PHP
#error_reporting(E_ALL);
header("Content-Type: text/html; charset=windows-1251");
$_GET = array();
$param = explode('/', $_SERVER['REQUEST_URI']);
if(isset($param[2]) and $param[2] != '') $_GET['unit'] = $param[2];
if(isset($param[3]) and $param[3] != '') $_GET['switch'] = $param[3];
$url = 'http://localhost/map/';
$community = 'public';
$unit = array(
'servernaya1' => array(
'router' => '10.10.10.1',
'switch' => array('192.168.1.2'),
),
'mashzal15' => array(
'router' => '10.10.10.2',
'switch' => array('192.168.2.2', '192.168.2.3'),
),
);
# Список используемых OID
$sysDescr = '.1.3.6.1.2.1.1.1'; # описание свича
$dot1dTpFdbAddress = '.1.3.6.1.2.1.17.4.3.1.1'; # список MAC адресов на свиче
$dot1dTpFdbPort = '.1.3.6.1.2.1.17.4.3.1.2'; # список расположения MAC по портам на свиче
$atPhysAddress = '.1.3.6.1.2.1.3.1.1.2'; # список соответствия MAC и IP взятый с маршрутизатора
echo <<<HERE
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<title>Список свичей</title>
<style>
body, table, tr, td {
font-style: normal;
font-family: verdana, arial, helvetica, sans-serif;
font-size: 11px;
}
body {
background-color: #FFEBD5;
}
tr, td {
padding: 2px;
}
#head {
font-weight: bold;
background-color: #9CB7D1;
margin: 2px;
border-radius: 2px;
}
td#head {
color: black;
}
a:link, a:visited, a:active {
text-decoration: underline;
color: white;
}
a:hover {
text-decoration: none;
color: white;
}
#activ {
color: #FFEBD5;
}
#port {
font-weight: bold;
background-color: #9CB7D1;
margin: 2px;
border-radius: 2px;
}
#num {
color: black;
}
td#title {
background-color: #CED7DF;
font-weight: bold;
border-radius: 2px;
color: black;
}
td#host1 {
font-family: Courier New, monospace;
font-weight: bold;
background-color: #F2F7FA;
border-radius: 2px;
color: black;
}
td#host2 {
font-family: Courier New, monospace;
font-weight: bold;
background-color: #E1E7EC;
border-radius: 2px;
color: black;
}
</style>
</head>
<body>
<table width="100%" id="head">
<tr>
<td width="100%">
<table width="100%" id="head">
<tr>
<td width="122">Список агрегатов:</td>
<td id="head">
HERE;
if(count($unit) > 0) {
foreach($unit as $name => $info) $u[] = '<a href="'.$url.$name.'"'.((isset($_GET['unit']) and $_GET['unit'] == $name)?' id="activ"':'').'>'.$name.'</a>';
}
echo isset($u)? implode(' ', $u) : 'Нет доступных агрегатов';
echo <<<HERE
</td>
</tr>
<tr>
<td width="122">Доступные свичи:</td>
<td id="head">
HERE;
if(count($unit[$_GET['unit']]['switch']) > 0 and isset($unit[$_GET['unit']])) {
foreach($unit[$_GET['unit']]['switch'] as $id => $name) {
$s[] = '<a href="'.$url.$_GET['unit'].'/'.$id.'"'.((isset($_GET['switch']) and $_GET['switch'] == $id)?' id="activ"':'').'>'.$name.'</a>';
}
echo isset($s)? implode(' ', $s) : 'Нет доступных свичей';
}
else {
echo 'Не выбран агрегат';
}
echo <<<HERE
</td>
</tr>
</table>
</td>
</tr>
</table>
<table width="100%" id="head">
<tr>
<td width="100%">
<table width="100%" id="head">
<tr>
<td id="head">
HERE;
if(isset($_GET['unit'], $_GET['switch'], $unit[$_GET['unit']]['switch'][$_GET['switch']])) {
$session = new SNMP(SNMP::VERSION_1, $unit[$_GET['unit']]['switch'][$_GET['switch']], $community);
$session->valueretrieval = SNMP_VALUE_LIBRARY;
$session->oid_output_format = SNMP_OID_OUTPUT_NUMERIC;
$session->oid_increasing_check = false;
foreach($session->walk($sysDescr) as $id => $val) {
echo (($name = str_replace('STRING: ', 'Свич ', $val)) != '""')? $name : 'Устройство не передало информацию о себе';
}
$session->close();
}
else echo ' ';
echo <<<HERE
</td>
</tr>
</table>
</td>
</tr>
</table>
HERE;
if(isset($_GET['unit'], $_GET['switch'], $unit[$_GET['unit']]['router'], $unit[$_GET['unit']]['switch'][$_GET['switch']])) {
$mac = $port = $map = array();
$session = new SNMP(SNMP::VERSION_1, $unit[$_GET['unit']]['router'], $community);
$session->valueretrieval = SNMP_VALUE_LIBRARY;
$session->oid_output_format = SNMP_OID_OUTPUT_NUMERIC;
$session->oid_increasing_check = false;
$info = @$session->walk($atPhysAddress);
if($session->getErrno() == 0 and count($info) > 0) {
foreach($info as $id => $val) {
$id = str_replace($atPhysAddress.'.', '', $id);
$val = explode(': ', $val);
$val = trim($val[1]);
# Велосипед, дабы не делать еще один запрос, а надо
$r = explode('.', $id);
unset($r[0], $r[1]);
$id = implode('.', $r);
$route[$val] = $id;
}
}
$session->close();
$session = new SNMP(SNMP::VERSION_1, $unit[$_GET['unit']]['switch'][$_GET['switch']], $community);
$session->valueretrieval = SNMP_VALUE_LIBRARY;
$session->oid_output_format = SNMP_OID_OUTPUT_NUMERIC;
$session->oid_increasing_check = false;
$info = @$session->walk($dot1dTpFdbPort);
if($session->getErrno() == 0 and count($info) > 0) {
foreach($info as $id => $val) {
$id = str_replace($dot1dTpFdbPort.'.', '', $id);
$val = explode(': ', $val);
$val = trim($val[1]);
if($val != 0) $port[$id] = $val;
}
}
$info = @$session->walk($dot1dTpFdbAddress);
if($session->getErrno() == 0 and count($info) > 0) {
foreach($info as $id => $val) {
$id = str_replace($dot1dTpFdbAddress.'.', '', $id);
$val = explode(': ', $val);
$val = trim($val[1]);
$table[$id] = $val;
if(isset($port[$id])) {
$map[$port[$id]][] = array('MAC' => $val, 'IP' => (isset($route[$val])? $route[$val] : 'неизвестен'));
}
}
}
$session->close();
if(isset($map) and count($map) > 0) {
ksort($map);
foreach($map as $port => $info) {
$i = 1;
echo '<table width="100%" id="port"><tr><td width="100%">';
echo '<table width="100%"><tr><td colspan="3"><b id="num">Порт #'.$port.'</b></td></tr>';
echo '<tr><td id="title" width="122">MAC</td><td id="title" width="122">IP</td><td id="title">Производитель</td></tr>';
foreach($info as $num => $host) {
$vendor = explode('(hex)', shell_exec("cat ./oui.txt | grep ".str_replace(' ', '-', substr($host['MAC'], 0, 8))));
$vendor = isset($vendor[1])? trim($vendor[1]) : 'неизвестен';
$css = ((($i++)%2!=0)?'1':'2');
echo '<tr><td id="host'.$css.'" width="122">'.$host['MAC'].'</td><td id="host'.$css.'" width="122">'.$host['IP'].'</td><td id="host'.$css.'">'.$vendor.'</td></tr>';
}
echo '</table>';
echo '</td></tr></table>';
}
}
else {
echo '<table width="100%" id="head"><tr><td width="100%"><table width="100%" id="head"><tr><td id="head">Свич не сообщил таблицу MAC адресов</td></tr></table></td></tr></table>';
}
}
else {
echo '<table width="100%" id="head"><tr><td width="100%"><table width="100%" id="head"><tr><td id="head">1. Выберите агрегат<br>2. Выберите интересующий Вас свич</td></tr></table></td></tr></table>';
}
echo <<<HERE
</body>
</html>
HERE;
?>
Сам скрипт. Конечно не красив, но для начала вполне сойдет.
Что нужно исправить:
1. Думаю в описании не нуждается
Код: Выделить всё
$url = 'http://localhost/map/';
$community = 'public';
2. Карта свичей. router содержит адрес маршрутизатора с которого будем брать ARP таблицу, а switch - список свичей в подсети с которых будем брать таблицу MAC адресов. servernaya1 и mashzal15 - названия агрегатов, будут фигурировать в адресах страницы.
Код: Выделить всё
$unit = array(
'servernaya1' => array(
'router' => '10.10.10.1',
'switch' => array('192.168.1.2'),
),
'mashzal15' => array(
'router' => '10.10.10.2',
'switch' => array('192.168.2.2', '192.168.2.3'),
),
);
oui.txt
Его содержимое можно взять отсюда
http://standards-oui.ieee.org/oui.txt
Файл необходимо для получения производителя железки в сети по MAC адресу.
Если есть желание, можно добавить запись в Cron и раз в месяц обновлять базу.
Для тех у кого проблемы с кодировкой, скидываю файл чтобы не заниматься копи-пастом.
PS: в целом все работает отлично. Рекомендую ограничить доступ к web морде. Могут быть проблемы с .htaccess в последней версии Апача под FreeBSD. У меня все прекрасно работает из коробки под Linux:
Apache/2.4.6 (Ubuntu)
PHP 5.5.3-1ubuntu2.6