Получение IP -, MAC –адресов, имени сетевого интерфейса с помощью Python

Порою возникают ситуации, когда необходимо использовать в скрипте Python IP-адрес используемый активной сетевой картой, которая смотрит в Интернет, узнать MAC-адрес этой карты и имя сетевого соединения. К сожалению, функций из коробки пока что не наблюдается. Есть сторонние модули, которые позволяют узнать MAC-адрес, например getmac, но в качестве параметров в них нужно также передавать или IP-адрес, или имя соединения. Но, что, если их нужно определять программно и вводить вручную не вариант? Я нашел для себя решение, которое работает, но требует тестирования на большом количестве систем. Хотя, думаю, что на большинстве ОС семейства Windows или Linux оно будет работать.

Импорт библиотек

В данном решении не требуется устанавливать сторонние библиотеки. Необходимо лишь импортировать в скрип те, что нужны для его работы. Выполним их импорт:

from platform import system from re import sub from subprocess import check_output from socket import socket, AF_INET, SOCK_DGRAM

Получение IP-адреса активной сетевой карты

Уже очень давно, около 15 лет назад на Stack Overflow был дан ответ по поводу получения «основного», имеющего маршрут по умолчанию, IP-адреса. Как описывает его автор скрипта, он работает под всеми основными ОС: Windows, Linux, OSX. Вот ссылка на данный пост.Несмотря на то, что скрипту уже довольно много лет, он работает до сих пор. Потому, вместо изобретения велосипеда я использую его, за неимением лучшего. Тем более что он ни разу не давал сбоев. Вот сам скрипт:

def local(): st = socket(AF_INET, SOCK_DGRAM) try: st.connect(('10.255.255.255', 1)) ip = st.getsockname()[0] except Exception: ip = '127.0.0.1' finally: st.close() return ip

Теперь, когда мы определились с тем, каким способом будем получать IP-адрес, приступим к написанию скрипта для получения IPv6-, MAC- адресов, а также имени сетевого интерфейса.

Получение информации об активном сетевом интерфейсе

Создадим класс NetInfo,который при инициализации будет получать необходимые данные в зависимости от операционной системы. Здесь мы определяем платформу с помощью модуля system библиотеки platform и в зависимости от этого запускаем тот или иной скрипт.

class NetInfo: def __init__(self): self.ipv4 = self.local() self.platform = system() self.mac = None self.iface = None self.ipv6 = None if self.platform == "Windows": self.mac_iface_win() elif self.platform == "Linux": self.mac_iface_lin() else: exit(0) else: exit(0)

Двигаемся дальше. Создадим функцию для получения локального IP-адерса и объявим ее статическим методом, так как в ней не используются переменные класса.

@staticmethod def local(): st = socket(AF_INET, SOCK_DGRAM) try: st.connect(('10.255.255.255', 1)) ip = st.getsockname()[0] except OSError: ip = '127.0.0.1' finally: st.close() return ip

Напишем функцию mac_iface_win(self) в которой выполним получение нужных нам параметров. Для этого мы будем использовать возможности командной строки Windows, с частности инструмент командной стоки wmic. В частности будем использовать псевдоним NICCONFIG, который используется для управления сетевыми адаптерами. Отфильтруем только активные сетевые адаптеры. А их может быть в системе несколько, включая виртуальные: IPEnabled=true . После этого получим MAC – и IP – адреса используя GET MACAddress, IPAddress /FORMAT:csv, с указанием вывода полученных значений в формате csv для того, чтобы нам было проще их распарсить.

Вот полный вид данной команды:

wmic NICCONFIG WHERE IPEnabled=true GET MACAddress, IPAddress /FORMAT:csv

После того, как мы получим список активных сетевых адаптеров, поитерируемся по нему в цикле и проверим, есть ли в данном списке адаптер, IP-адрес которого равен полученному ранее локальному адресу. Если есть, забираем MAC-адрес, а также IPv6 адрес, если он не отключен в системе.

После того, как мы получим необходимые данные, выполним команду getmac /FO csv /NH /V с помощью которой получим список сетевых интерфейсов. Также в цикле проитерируемся по нему и будем проверять, есть ли уже полученный MAC-адрес в строке с параметрами интерфейса. Если есть, забираем название сетевого интерфейса.

def mac_iface_win(self): adapter_lst = check_output("wmic NICCONFIG WHERE IPEnabled=true GET MACAddress, " "IPAddress /FORMAT:csv", shell=False).decode().strip().splitlines() for adapter in adapter_lst: if adapter.strip(): node, ipaddr, mac = adapter.split(",") ipaddr = sub("[{}]", "", ipaddr).split(";") if ipaddr[0] == self.ipv4: self.mac = mac.upper() try: self.ipv6 = ipaddr[1] except IndexError: pass if self.mac: interface_all = check_output('getmac /FO csv /NH /V', shell=False).decode('cp866').splitlines() for line in interface_all: if self.mac.upper().replace(":", "-") in line: self.iface = line.split(",")[0].replace('"', '') break

Если в предыдущей функции мы получили данные для сетевого интерфейса в ОС Windows, то следует также написать аналогичную функцию и для Linux. Поэтому создадим функцию mac_iface_lin(self). В ней кода будет поменьше, так как в командах Linux содержится больше информации в одном месте и ее легче распарсить. Команда, которую мы будем использовать выглядит следующим образом:

ip -h -br a | grep UP

Здесь мы получим название сетевого интерфейса и IPv6-адрес. В Linux данный адрес можно получить, даже если он отключен в настройках адаптера.После этого выполним похожую команду, но уже отфильтруем из ее вывода MAC-адрес:

ip a | grep ether | gawk '{print $2}'

Осталось только свести написанный код, если вы этого еще не сделали воедино.

def mac_iface_lin(self): com_run = check_output('ip -h -br a | grep UP', shell=True).decode().split() self.iface = com_run[0].strip() self.ipv6 = com_run[3].strip().split("/")[0] self.mac = check_output("ip a | grep ether | gawk '{print $2}'", shell=True).decode().strip().upper()

Итак, продолжим. Полный код скрипта выглядит следующим образом:

from platform import system from re import sub from subprocess import check_output from socket import socket, AF_INET, SOCK_DGRAM class NetInfo: def __init__(self): self.ipv4 = self.local() self.platform = system() self.mac = None self.iface = None self.ipv6 = None if self.platform == "Windows": self.mac_iface_win() elif self.platform == "Linux": self.mac_iface_lin() else: exit(0) @staticmethod def local(): st = socket(AF_INET, SOCK_DGRAM) try: st.connect(('10.255.255.255', 1)) ip = st.getsockname()[0] except OSError: ip = '127.0.0.1' finally: st.close() return ip def mac_iface_win(self): adapter_lst = check_output("wmic NICCONFIG WHERE IPEnabled=true GET MACAddress, " "IPAddress /FORMAT:csv", shell=False).decode().strip().splitlines() for adapter in adapter_lst: if adapter.strip(): node, ipaddr, mac = adapter.split(",") ipaddr = sub("[{}]", "", ipaddr).split(";") if ipaddr[0] == self.ipv4: self.mac = mac.upper() try: self.ipv6 = ipaddr[1] except IndexError: pass if self.mac: interface_all = check_output('getmac /FO csv /NH /V', shell=False).decode('cp866').splitlines() for line in interface_all: if self.mac.upper().replace(":", "-") in line: self.iface = line.split(",")[0].replace('"', '') break def mac_iface_lin(self): com_run = check_output('ip -h -br a | grep UP', shell=True).decode().split() self.iface = com_run[0].strip() self.ipv6 = com_run[3].strip().split("/")[0] self.mac = check_output("ip a | grep ether | gawk '{print $2}'", shell=True).decode().strip().upper()

Теперь необходимо протестировать его в операционных системах. В данном случае у меня доступны две системы: Windows 10 и Linux Mint.

Создадим в данном скрипте вызов нашего класса и выведем в терминал полученные параметры:

name = NetInfo().iface ip_ = NetInfo().ipv4 ip_6 = NetInfo().ipv6 mac_ = NetInfo().mac print(f'{name} | {ip_} | {ip_6} | {mac_}')

Для начала, запустим в ОС Windows:

А теперь то же самое в Linux Mint:

Как видим, скрипт справляется со своей работой. Для чего он может пригодиться? Ну, например, для автоматической установки активного сетевого интерфейса по умолчанию в Scapy при прослушивании пакетов с активного сетевого интерфейса. Но о Scapy поговорим немного позже.

Спасибо за внимание. Надеюсь, данная информация будет вам полезна!

0
Комментарии
-3 комментариев
Раскрывать всегда