Поиск коммутатора и порта по мак адресу хоста

Всем доброго дня.

Прежде всего, моя статья адресована администраторам крупных корпоративных сетей (построенных исключительно на оборудовании Cisco), в которых может насчитываться до 16000 ethernet портов. В таких сетях многие, казалось бы, элементарные вещи начинают со временем сильно утомлять.

К примеру, в крупных компаниях многим пользователям почему-то не сидится на местах и они начинают мигрировать с кабинета в кабинет, с этажа на этаж…, а это значит, что их хосты подключается в новые розетки и не факт, что эти пользователи после переезда окажутся в своем VLAN. Это в свою очередь влечет за собой следующее: почти каждый день в Service Desk приходят наряды такого содержания: «Просьба перевести такой-то MAC адрес в таком-то здании в такую-то сеть».

И вроде бы мелочь, но если она повторяется каждый день, то наступает момент, когда количество переходит в качество и подобные наряды ничего кроме раздражения не вызывают. Не знаю как остальные администраторы, но я терпеть не могу рутину в любом ее проявлении. Поэтому написал полезную утилиту на перле: fport.pl.

Пока в утилите минимальный функционал: она ищет порт и коммутатор по мак адресу и текущий VLAN порта. Утилита найденный порт не переводит в другой VLAN (пока я тестирую данную утилиту на предмет багов, т.к. в коде не всегда получается предусмотреть заранее все нюансы, а если случайно утилита переведет транковый порт… в общем не очень кошерно).

Формат задания аргументов для утилиты следующий:

./fport.pl “object1|mac1,mac2,mac3…” “object_2|mac1,mac2…” …

Формат задания MAC адреса: 2 последних байта либо все 6 байт, разделенных точками через каждые 2 байта; регистр не важен, между байтами могут быть дефисы или двоеточия.

Примеры:
0019.dbab.d5cf
00:19.db:ab.d5:cf
00-19.db-ab.d5-cf
001D.928A.9CD0
00:1D.92:8A.9C:D0

00-1D.92-8A.9C-D0
0c54
0c:54
0c-54
8EF3
8E:F3
8E-F3

Пример вывода (настоящие значения заменены псевдозначениями):

./fport.pl «XXX|zz:zz,nn:nn» «YYYY|mm-mm.mm-mm.mm-mm»
Searching MAC in the bulding block XXX:
MAC zzzz not found!
MAC nnnn found on device: x.x.x.x on port: Gix/y/z, now MAC in VLAN: xxx.
Searching MAC in the bulding block YYYY:
MAC mmmm.mmmm.mmmm found on device: y.y.y.y on port: Gix/y/z, now MAC in VLAN: yyy.

Напутствия:

Перед использованием данной утилиты должна быть создана база данных как минимум с тремя таблицами:

1. init содержит соответствие: id_объекта – ip_адрес_distribution:

init:
+———+———+
| obj_id | dev_ip |
+———+———+
| 1 | 3.3.3.3 |
| 2 | 2.2.2.2 |
+———+———+

2. aliases содержит список различных псевдонимов объекта, т.к. на предприятии коллеги часто могут использовать не только формально название объекта, но и всевозможные сокращения: id_объекта – псевдоним_объекта:

aliases:
+———+————-+
| obj_id | alias |
+———+————-+
| 1 | Mira |
| 2 | Vernadskogo |
| 2 | Vern |

+———+————-+

3. objects содержит соответствие: id_объекта – название_объекта:

objects:
+———+————————-+
| obj_id | object |
+———+————————-+
| 1 | Prospekt Mira, 101 |
| 2 | Prospekt Vernadskogo, 5 |

+———+————————-+

Также должен работать протокол CDP на всех транках.

Немного о принципе работы: fport.pl выделяет аргументы, по очереди выбирает из базы данных необходимые ip адреса соответствующих distribution устройств и, используя данные какой-либо учетный записи в TACAS, по очереди ищет все маки на данном объекте. Процесс поиска представляет собой рекурсивный парсинг вывода следующих команд: sh mac- mac, sh cdp neighbors port_name detail, sh etherchannel summary | i Po_number (вывод команды sh etherchannel summary используется для случая, когда на порту настроена агрегация).

Более подробно с алгоритмом работы можно ознакомиться из исходного кода под катом (в коде присутствуют основные комментарии).

Вот собственно сам код.

Ставьте perl, mysql создавайте базу и таблицы с описанным выше интерфейсом и все будет ok:

#!/usr/bin/perl
 use strict;
 use DBI;
 #Чтобы подключатся к устройствам Cisco необходим этот модуль.
 use Net::Telnet::Cisco;
 our ($acs_login, $acs_password, $db_name, $db_login, $db_password, $dbh);
 #Инициализируем логины и пароли.
 &init_accounts;
 #Создем подключение к нашей базе.
 $dbh = &db_connect ($db_name, $db_login, $db_password);
 #По очереди обрабатываем перечисленные объекты.
 foreach my $arg (@ARGV) {
 my @MAC = (); 
 (my $object, my $MAC, my $dev_ip) = (undef, undef, undef);
 ($object, $MAC) = split(/\|/, $arg);
 @MAC = split(/,/, $MAC);
 #Извлекаем из базы ip дистрибьюшена.
 $dev_ip = &get_dev_ip($object);
 print "Searching MAC in the bulding block $object:\n";
 foreach my $mac (@MAC) {
 #удаляем ненужные символы и преобразуем MAC к нижнему регистру.
 $mac =~ tr/ABCDEF/abcdef/;
 $mac =~ s/[-:]//g;
 #Начинаем рекурсивный поиск MAC адреса, передаем этой функции ip дистрибьюшена и искомый MAC адрес.
 (my $ip, my $port, my $current_vlan) = &find_port_by_mac ($dev_ip, $mac);
 if ($ip and $port and $current_vlan) {
 print " MAC $mac found on device: $ip on port: $port, now MAC in VLAN: $current_vlan.\n";
 }
 else {
 print " MAC $mac not found!\n";
 }
 }
 }
 print "\n\nCompleted!\n";
 sub init_accounts {
 $acs_login = 'ram';
 $acs_password = 'kexdhftuj';
 $db_name = 'Lukoil';
 $db_login = "root";
 $db_password = "1234567890"; 
 }
 sub db_connect {
 my ($db_name, $db_login, $db_password, $data_source, $dbh);
 ($db_name, $db_login, $db_password) = @_;
 $data_source = "DBI:mysql:$db_name:localhost";
 $dbh = DBI->connect($data_source, $db_login, $db_password);
 return $dbh;
 }
 sub get_dev_ip {
 my ($object, $dev_ip, $subquery, $request, $sth, $ref);
 ($object) = @_;
 $subquery = "select obj_id from aliases where alias in ('$object')";
 $request = "select obj_id,dev_ip from init where obj_id in ($subquery) group by obj_id;";
 $sth = $dbh->prepare($request);
 $sth->execute();
 $ref = $sth->fetchall_arrayref(); 
 $dev_ip = ${${$ref}[0]}[1];
 return $dev_ip;
 }
 sub find_port_by_mac { my ($session, $dev_ip, $ip, $int, $vl, $port, $vlan, $neighbor_ip, $sh_mac, $mac);
 ($dev_ip, $mac) = @_;
 #Подключаемся к дистрибьюшену
 if (eval {$session = Net::Telnet::Cisco->new(Host=>"$dev_ip", Timeout =>"25"); $session->login(Name => "$acs_login", Password => "$acs_password");}) {
 #получаем информацию о том, за каким портом виден данный MAC.
 ($port, $vlan) = &get_port_to_neighbor($session, $mac);
 #получаем информацию о том, за каким портом виден данный MAC.
 if ($port) { 
 #Смотрим, виден ли коммутатор по CDP за этим портом или нет, если виден, то рекурсивно идем на него и т.д.
 $neighbor_ip = &get_neighbor_ip($session, $port);
 if ($neighbor_ip) {
 ($dev_ip, $port, $vlan) = &find_port_by_mac($neighbor_ip, $mac);
 }
 }
 $session->close;
 return ($dev_ip, $port, $vlan);
 }
 }
 sub get_port_to_neighbor {
 my ($session, $vlan, $mac, $sh_mac, $port, $Po, $etherchannel);
 ($session, $mac) = @_;
 if (eval {($sh_mac) = $session->cmd("sh mac- | i $mac");}) {
 $sh_mac =~ s/^(\n+)//;
 $sh_mac =~ s/(\n+)$//;
 $sh_mac =~ s/^(\s+)//;
 $sh_mac =~ s/(\s+)$//;
 if ($sh_mac =~ /(\d{1,3})\s+([a-f0-9]{4}\.){2}[a-f0-9]{4}.+([A-Za-z]{2})\s*((\d+\/)+\d+)/) {
 if ($1) {$vlan = $1;}
 if ($3 and $4) {$port = $3.$4;}
 }
 elsif ($sh_mac =~ /(\d{1,3})\s+([a-f0-9]{4}\.){2}[a-f0-9]{4}.+(Po\d+)/) {
 if ($1) {$vlan = $1;}
 if ($3) {$Po = $3;}
 if (eval {($etherchannel) = $session->cmd("sh etherchannel summary | i $Po");}) {
 $etherchannel =~ /([A-Za-z]{2}\s*(\d+\/)+\d+)/;
 if ($1) {$port = $1;}
 }
 }
 return ($port, $vlan);
 }
 }
 sub get_neighbor_ip {
 my (@sh_cdp_n_detail, $session, $sh_cdp_n_detail, $port, $neighbor_ip, $ip_phone);
 ($session, $port) = @_;
 $ip_phone = 1;
 if (eval {@sh_cdp_n_detail = $session->cmd("sh cdp neighbors $port detail");}) {
 foreach $sh_cdp_n_detail (@sh_cdp_n_detail) {
 $sh_cdp_n_detail =~ s/^(\n+)//;
 $sh_cdp_n_detail =~ s/(\n+)$//;
 $sh_cdp_n_detail =~ s/^(\s+)//;
 $sh_cdp_n_detail =~ s/(\s+)$//;
 if ($sh_cdp_n_detail =~ /[Ii][Pp]\s*address\s*:\s*((\d{1,3}\.){3}\d{1,3})/){
 if ($1) {$neighbor_ip = $1;}
 }
 #Убеждаемся, что соседнее устройство не IP телефон
 elsif ($sh_cdp_n_detail =~ /SEP|SIP/){
 $ip_phone = 0;
 }
 }
 if ($ip_phone) {return $neighbor_ip;}
 else {return 0;}
 }
 }

Источник: http://ajc.su/seti/poisk-kommutatora-i-porta-po-mak-adresu-xosta/

Запись опубликована в рубрике *Lan&Wan, *Unix,*Linux, *Сети. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Я не спамер This plugin created by Alexei91