Получение 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 поговорим немного позже.
Спасибо за внимание. Надеюсь, данная информация будет вам полезна