Визуализация топологии сети с помощью скрипта Python и NeXt UI
В данный момент в сети существует немало утилит и программ с открытым кодом для визуализации сетевых топологий. Исходными данными, как правило, для них выступают данные таких протоколов, как Cisco Discovery Protocol (CDP), Link Layer Discovery Protocol (LLDP), Open Shortest Path First (OSPF), Intermediate System to Intermediate System (ISIS).
Реализации CDP и LLDP могут использоваться в качестве решения для небольших доменов, с ограниченным количеством сетевых устройств. В отношении CDP, следует заметить, что данный протокол является исключительно проприетарным и может применяться в сетях только с оборудованием Cisco.
OSPF и ISIS относятся к категории Interior Gateway Protocol (IGP), являются по сути Link State протоколами, и их базы Link State Database (LSDB) содержат информацию о всех хостах домена, состоянии и типе линков, метриках для расчета дерева кратчайшего пути (shortest-path tree, используется алгоритм Dijkstra), т. е. всю необходимую информацию о топологии сети. Любой из этих протоколов в большинстве случаев является неотъемлемым атрибутом дизайна любой сети, будь-то Internet Service Provider, Data Center или Enterprise.
В этой статье речь пойдет о небольшом скрипте Python и наборе шаблонов TextFSM, с помощью которых, в связке с open source решением NeXt UI (применяется в SDN контроллере ODL), используя LSDB протоколов ISIS, OSPF, легко и просто визуализировать топологию сети, содержащую от несколько десятков до несколько сотен устройств. Следует отметить, что решение может применяться в мультивендорных сетях (в текущей версии поддерживаются протоколы следующих вендоров: Nokia — OSPF и ISIS;Juniper — OSPF и ISIS; Cisco — OSPF и ISIS; Huawei — ISIS).
Для запуска скрипта необходимо:
1. Загрузить все содержимое из git clone:
2. Зайти в каталог igp_topo_multi и проверить его содержимое:
3. Отредактировать файл config.yaml, используя текстовый редактор:
В нем необходимо указать тип хоста (nokia, juniper, cisco, huawei), протокол (ospf или isis), путь и название текстового файла с выводом LSDB (используется каталог test_files, в котором уже содержится несколько файлов с тестовыми топологиями). Сохранить данные и закрыть редактор:
5. В открывшемся окне браузера по умолчанию, отобразится топология сети:
- Загрузку файлов из github.com желательно выполнять в пользовательский каталог без sudo. В противном случае скрипт не сможет запустить браузер. Если браузер не запустится по каким-либо причинам, страницу index.html, которая находится в каталоге nextUI, можно открыть самостоятельно.
- Пиктограммы маленьких коммутаторов в топологии обозначают broadcast линки (не р2р). Повод задуматься, оправдан ли этот тип для каждого конкретного случая.
- Цветовые темы NeXt UI можно применять разные из доступных.
- Положение хостов в браузере можно изменять их перетаскиванием.
Статья Сканер локальной сети для получения списка IP и MAC адресов на Python
На данный момент в различных операционных системах существует довольно много самых разнообразных сканеров сети, которые не только составят вам ее карту, но, также покажут, какие сетевые ресурсы доступны на каждой из найденных машин, их сетевые имена. Но, почему бы не сделать свой сканер на Python, который будет составлять карту сети, хоть и без сетевых ресурсов на данном этапе, но, тем не менее, работать он будет довольно быстро и в качестве практики очень даже полезно.
Для того, чтобы создать быстрый сканер сети, с возможностью его дальнейшего расширения, нам понадобиться установить scapy. Это многофункциональная библиотека, которая позволяет отправлять, анализировать и подделывать сетевые пакеты, а также сканировать или атаковать сети. Установка ее достаточно простая. Нужно написать в терминале команду:
pip install —pre scapy[basic]
Однако, это не единственный, но рекомендуемый, вариант установки данной библиотеки. Вот небольшая табличка с командами по установке разных по компоновке пакетов. Для наших целей будет вполне достаточно рекомендуемой установки.
Если ваша операционная система отличается от Linux, а в данном случае имеется в виду Windows, то потребуется установить библиотеку захвата пакетов Npcap. Если же вы используете Windows XP, хотя на сегодняшнее время это вряд ли, то нужно использовать библиотеку WinPcap. Установить Npcap можно скачав его
На этом подготовительный этап можно завершить. Давайте теперь импортируем scapy в наш скрипт.
from platform import system from socket import socket, AF_INET, SOCK_DGRAM from subprocess import check_output import os import scapy.all as sc
Как видите, помимо библиотеки scapy для скрипта нужно будет импортировать еще несколько модулей входящих в состав стандартных библиотек python. В данном случае библиотека platform будет нужна для определения операционной системы. Так как данный скрипт будет работать как в Windows, так и в Linux. Библиотека socket используется для получения локального IP-адреса компьютера, а subprocess для выполнения команды и тем самым получения IP-адреса роутера, используемого в системе по умолчанию.
Получаем локальный IP-адрес
Давайте приступим к созданию сканера сети. Для начала нам будет нужна функция, которая получает локальный IP-адрес машины. Создадим ее и назовем, к примеру: local_ipv4(). Данная функция устанавливает соединение с адресом 10.255.255.255, а затем с помощью getsockname возвращает адрес и порт сокета. В данном случае порт нам не нужен, а вот адрес вполне пригодится. Поэтому забираем его из кортежа и возвращаем из функции. Если же произошла ошибка и соединение не удалось, возвращается адрес 127.0.0.1. Что, в принципе, будет соответствовать действительности, так как в данном случае сетевого соединения не будет, а будет использоваться только localhost.
def local_ipv4(): st = socket(AF_INET, SOCK_DGRAM) try: st.connect(('10.255.255.255', 1)) ip_l = st.getsockname()[0] except Exception: ip_l = '127.0.0.1' finally: st.close() return ip_l
Дополнение: функцию получения локального адреса можно сделать немного по другому, но только в том случае, если у вас ОС Linux. Нужно дополнительно установить библиотеки getmac и netifaces.
pip install getmac netifaces
from getmac.getmac import _get_default_iface_linux from netifaces import ifaddresses, AF_INET
Тогда функция будет выглядеть вот так:
def local_ipv4(): try: return ifaddresses(_get_default_iface_linux()).setdefault(AF_INET)[0]['addr'] except TypeError: return '127.0.0.1'
Получение IP-адреса шлюза используемого в системе по умолчанию
На следующем этапе нужно получить IP-адрес шлюза, который используется в системе по умолчанию. Для этого используем subprocess.check_output, которая запускает выполнение определенной команды в терминале и возвращает ее вывод. И для Linux, и для Windows будем использовать похожие команды. Для Linux это «route -n», а вот с Windows все чуточку посложнее, так как там при выполнении команды возвращается куча всяких не особо то нужных в данном случае параметров. Поэтому нужно будет выполнить среди всей этой информации поиск и вывести уже отфильтрованный результат.
Давайте сделаем первую функцию для Windows. Создадим get_gateway_win(). На вход она не получает никаких данных, а на выходе возвращает адрес шлюза. С помощью subprocess.check_output выполняется команда, после чего результат приводится к нужному виду и возвращается из функции.
def get_gateway_win(): # получаем адрес шлюза по умолчанию для текущего сетевого интерфейса com = f'route PRINT 0* | findstr '.split() return check_output(com, shell=True).decode('cp866').split()[2]
И для Linux, создадим функцию get_gateway_linx(). Принцип получения адреса шлюза здесь тот же самый и на выходе получается нужный адрес шлюза.
def get_gateway_linx(): com = 'route -n'.split() ip_route = str(subprocess.check_output(com, shell=True)).split("\\n")[2].split()[1].strip() if ip_route.isdigit(): return ip_route else: sock = socket.gethostbyname(ip_route) return sock
Теперь самое время приступить к сканированию сети. Создадим функцию get_ip_mac_nework(ip), которая на входе будет получать начальный адрес для сканирования с маской подсети, а возвращать список из словарей с полученными значениями ip и mac адресов. Сделаем с помощью функции scapy.srp широковещательный ARP запрос, чтобы получить ответы от всех компьютеров, которые находятся с нами в одной сети. Для отправки запроса нужно сформировать пакет, который будет состоять из широковещательного адреса и ARP-пакета, в котором указывается адрес и маска подсети. Параметр timeout указывает на то, что между запросами нужно делать паузу в одну секунду, а параметр verbose позволяет не выводить результаты выполнения запроса в терминал.
answered_list = sc.srp(sc.Ether(dst=’ff:ff:ff:ff:ff:ff’) / sc.ARP(pdst=ip), timeout=1, verbose=False)[0]
С помощью данного запроса мы можем получить два списка. С запросами, на которые был получен ответ и с запросами, на которые ответ не был получен. Так как нам не отвеченные запросы вроде бы без надобности, забираем только те, ответ на которые был получен. Потому, указываем явно, что забираем 0 элемент.
Далее в цикле пробежимся по содержимому списка с запросами и получим из него нужные значения, которые добавим в список, возвращаемый из функции в виде словаря.
def get_ip_mac_nework(ip): answered_list = sc.srp(sc.Ether(dst='ff:ff:ff:ff:ff:ff') / sc.ARP(pdst=ip), timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: clients_list.append() return clients_list
Печать результатов сканирования
Ну и осталось вывести полученные результаты на экран. В данном случае за это будет отвечать функция print_ip_mac(mac_ip_list), которая на входе получает сформированный ранее список со словарями. Тут все просто. Пробегаемся в цикле по списку и получаем определенные значений из словарей под нужным индексом. После чего выводим на экран.
def print_ip_mac(mac_ip_list): print(f"\nMachine in Network:\n\nIP\t\t\t\t\tMAC-address\n") for client in mac_ip_list: print(f'\t\t')
Запуск сканирования сети
Для запуска сканирования сети, а также получения ip адресов будем использовать функцию main(). Для начала получим локальный ip, затем, в зависимости от системы ip шлюза. Сформируем из полученного локального IP-адреса адрес сети с маской и запустим функцию сканирования сети. После чего выведем на экран IP-адреса и результаты сканирования.
def main(): local_ip = local_ipv4() if system() == "Windows": gateway = get_gateway_win() elif system() == 'Linux': if not os.getuid() == 0: print('\n[+] Запустите скрипт с правами суперпользователя!\n') return gateway = get_gateway_linx() ip_mac_network = get_ip_mac_nework(f'...1/24') print(f'\n[+] Local IP: \n[+] Local Gateway: ') print_ip_mac(ip_mac_network)
А на этом, в данном случае все. Сканер сети готов, осталось только посмотреть на результаты его работы. Запускаем и видим, что в нашей локальной сети есть межсетевой экран. А те, кто понимает, даже узнали, что это pfSense, а также три машины ip и mac адреса которых были благополучно получены.
from platform import system from socket import socket, AF_INET, SOCK_DGRAM, gethostbyname from subprocess import check_output import os import scapy.all as sc # получаем локальный IP-адрес def local_ipv4(): st = socket(AF_INET, SOCK_DGRAM) try: st.connect(('10.255.255.255', 1)) ip_l = st.getsockname()[0] except Exception: ip_l = '127.0.0.1' finally: st.close() return ip_l def get_gateway_win(): # получаем адрес шлюза по умолчанию для текущего сетевого интерфейса в Windows com = f'route PRINT 0* | findstr '.split() return check_output(com, shell=True).decode('cp866').split()[2] # получение ip адреса шлюза используемого по умолчанию в Linux def get_gateway_linx(): ip_route = str(check_output('route -n | grep UG', shell=True).decode()).split()[1].strip() return ip_route # сканируем сеть, получаем ip и mac сетевых машин def get_ip_mac_nework(ip): answered_list = sc.srp(sc.Ether(dst='ff:ff:ff:ff:ff:ff') / sc.ARP(pdst=ip), timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: clients_list.append() return clients_list # функция печати сканированных ip и mac def print_ip_mac(mac_ip_list): print(f"\nMachine in Network:\n\nIP\t\t\t\t\tMAC-address\n") for client in mac_ip_list: print(f'\t\t') def main(): local_ip = local_ipv4() if system() == "Windows": gateway = get_gateway_win() elif system() == 'Linux': if not os.getuid() == 0: print('\n[+] Запустите скрипт с правами суперпользователя!\n') return gateway = get_gateway_linx() ip_mac_network = get_ip_mac_nework(f'...1/24') print(f'\n[+] Local IP: \n[+] Local Gateway: ') print_ip_mac(ip_mac_network) if __name__ == "__main__": main()
Ну и да, не забывайте о том, что именно таким образом вам удастся просканировать только локальную сеть.
Спасибо за внимание. Надеюсь, что данная информация была кому-нибудь полезной