In Python, is there a way to detect whether a given network interface is up?
In my script, the user specifies a network interface, but I would like to make sure that the interface is up and has been assigned an IP address, before doing anything else.
I'm on Linux and I am root.
The interface can be configured with an IP address and not be up so the accepted answer is wrong. You actually need to check /sys/class/net/<interface>/flags. If the content is in the variable flags, flags & 0x1 is whether the interface is up or not.
Depending on the application, the /sys/class/net/<interface>/operstate might be what you really want, but technically the interface could be up and the operstate down, e.g. when no cable is connected.
All of this is Linux-specific of course.
As suggested by #Gabriel Samfira, I used netifaces. The following function returns True when an IP address is associated to a given interface.
def is_interface_up(interface):
addr = netifaces.ifaddresses(interface)
return netifaces.AF_INET in addr
The documentation is here
Answer using psutil:
import psutil
import socket
def check_interface(interface):
interface_addrs = psutil.net_if_addrs().get(interface) or []
return socket.AF_INET in [snicaddr.family for snicaddr in interface_addrs]
With pyroute2.IPRoute:
from pyroute2 import IPRoute
ip = IPRoute()
state = ip.get_links(ip.link_lookup(ifname='em1'))[0].get_attr('IFLA_OPERSTATE')
ip.close()
With pyroute2.IPDB:
from pyroute2 import IPDB
ip = IPDB()
state = ip.interfaces.em1.operstate
ip.release()
You can see the content of the file in /sys/class/net/<interface>/operstate. If the content is not down then the interface is up.
If the question is about checking if the cable is conencted (FreeBSD);
[status for status in run.cmd(' /usr/local/bin/sudo ifconfig %s ' % interface).split("\t") if status.strip().startswith("status")][0].strip().endswith("active")
For this, no api support so far :( ...
Related
I need a cross platform method of determining the MAC address of a computer at run time. For windows the 'wmi' module can be used and the only method under Linux I could find was to run ifconfig and run a regex across its output. I don't like using a package that only works on one OS, and parsing the output of another program doesn't seem very elegant not to mention error prone.
Does anyone know a cross platform method (windows and linux) method to get the MAC address? If not, does anyone know any more elegant methods then those I listed above?
Python 2.5 includes an uuid implementation which (in at least one version) needs the mac address. You can import the mac finding function into your own code easily:
from uuid import getnode as get_mac
mac = get_mac()
The return value is the mac address as 48 bit integer.
The pure python solution for this problem under Linux to get the MAC for a specific local interface, originally posted as a comment by vishnubob and improved by on Ben Mackey in this activestate recipe
#!/usr/bin/python
import fcntl, socket, struct
def getHwAddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15]))
return ':'.join(['%02x' % ord(char) for char in info[18:24]])
print getHwAddr('eth0')
This is the Python 3 compatible code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import fcntl
import socket
import struct
def getHwAddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
return ':'.join('%02x' % b for b in info[18:24])
def main():
print(getHwAddr('enp0s8'))
if __name__ == "__main__":
main()
netifaces is a good module to use for getting the mac address (and other addresses). It's crossplatform and makes a bit more sense than using socket or uuid.
import netifaces
netifaces.interfaces()
# ['lo', 'eth0', 'tun2']
netifaces.ifaddresses('eth0')[netifaces.AF_LINK]
# [{'addr': '08:00:27:50:f2:51', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]
pypi location
Good Intro to netifaces
Sometimes we have more than one net interface.
A simple method to find out the mac address of a specific interface, is:
def getmac(interface):
try:
mac = open('/sys/class/net/'+interface+'/address').readline()
except:
mac = "00:00:00:00:00:00"
return mac[0:17]
to call the method is simple
myMAC = getmac("wlan0")
One other thing that you should note is that uuid.getnode() can fake the MAC addr by returning a random 48-bit number which may not be what you are expecting. Also, there's no explicit indication that the MAC address has been faked, but you could detect it by calling getnode() twice and seeing if the result varies. If the same value is returned by both calls, you have the MAC address, otherwise you are getting a faked address.
>>> print uuid.getnode.__doc__
Get the hardware address as a 48-bit positive integer.
The first time this runs, it may launch a separate program, which could
be quite slow. If all attempts to obtain the hardware address fail, we
choose a random 48-bit number with its eighth bit set to 1 as recommended
in RFC 4122.
Using my answer from here: https://stackoverflow.com/a/18031868/2362361
It would be important to know to which iface you want the MAC for since many can exist (bluetooth, several nics, etc.).
This does the job when you know the IP of the iface you need the MAC for, using netifaces (available in PyPI):
import netifaces as nif
def mac_for_ip(ip):
'Returns a list of MACs for interfaces that have given IP, returns None if not found'
for i in nif.interfaces():
addrs = nif.ifaddresses(i)
try:
if_mac = addrs[nif.AF_LINK][0]['addr']
if_ip = addrs[nif.AF_INET][0]['addr']
except IndexError, KeyError: #ignore ifaces that dont have MAC or IP
if_mac = if_ip = None
if if_ip == ip:
return if_mac
return None
Testing:
>>> mac_for_ip('169.254.90.191')
'2c:41:38:0a:94:8b'
You can do this with psutil which is cross-platform:
import psutil
nics = psutil.net_if_addrs()
print [j.address for j in nics[i] for i in nics if i!="lo" and j.family==17]
The cross-platform getmac package will work for this, if you don't mind taking on a dependency. It works with Python 2.7+ and 3.4+. It will try many different methods until either getting a address or returning None.
from getmac import get_mac_address
eth_mac = get_mac_address(interface="eth0")
win_mac = get_mac_address(interface="Ethernet 3")
ip_mac = get_mac_address(ip="192.168.0.1")
ip6_mac = get_mac_address(ip6="::1")
host_mac = get_mac_address(hostname="localhost")
updated_mac = get_mac_address(ip="10.0.0.1", network_request=True)
Disclaimer: I am the author of the package.
Update (Jan 14 2019): the package now only supports Python 2.7+ and 3.4+. You can still use an older version of the package if you need to work with an older Python (2.5, 2.6, 3.2, 3.3).
Note that you can build your own cross-platform library in python using conditional imports. e.g.
import platform
if platform.system() == 'Linux':
import LinuxMac
mac_address = LinuxMac.get_mac_address()
elif platform.system() == 'Windows':
# etc
This will allow you to use os.system calls or platform-specific libraries.
To get the eth0 interface MAC address,
import psutil
nics = psutil.net_if_addrs()['eth0']
for interface in nics:
if interface.family == 17:
print(interface.address)
nice actually return a dictionary within the dictionary it returns a list and within the list, it returns a staple. but nics['Ethernet'][0].address iteration solve the problem.
import psutil
nics = psutil.net_if_addrs()
mac_address = nics['Ethernet'][0].address
print(mac_address)
This cross-platform code does not 100% work on Windows. This works on Windows:
import psutil
print([(k, addr.address) for k, v in psutil.net_if_addrs().items() for addr in v if addr.family == -1])
Example:
[
('Local Area Connection', '01-23-45-67-89-0A'),
('Wireless Network Connection', '23-45-67-89-0A-BC'),
('Bluetooth Network Connection', '45-67-89-0A-BC-DE'),
('isatap.{01ABCDEF-0123-4567-890A-0123456789AB}', '00-00-00-00-00-00-00-01')
]
I dont know of a unified way, but heres something that you might find useful:
http://www.codeguru.com/Cpp/I-N/network/networkinformation/article.php/c5451
What I would do in this case would be to wrap these up into a function, and based on the OS it would run the proper command, parse as required and return only the MAC address formatted as you want. Its ofcourse all the same, except that you only have to do it once, and it looks cleaner from the main code.
Alternatively,
import uuid
mac_id=(':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff)
For Linux let me introduce a shell script that will show the mac address and allows to change it (MAC sniffing).
ifconfig eth0 | grep HWaddr |cut -dH -f2|cut -d\ -f2
00:26:6c:df:c3:95
Cut arguements may dffer (I am not an expert) try:
ifconfig etho | grep HWaddr
eth0 Link encap:Ethernet HWaddr 00:26:6c:df:c3:95
To change MAC we may do:
ifconfig eth0 down
ifconfig eth0 hw ether 00:80:48:BA:d1:30
ifconfig eth0 up
will change mac address to 00:80:48:BA:d1:30 (temporarily, will restore to actual one upon reboot).
For Linux you can retrieve the MAC address using a SIOCGIFHWADDR ioctl.
struct ifreq ifr;
uint8_t macaddr[6];
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
return -1;
strcpy(ifr.ifr_name, "eth0");
if (ioctl(s, SIOCGIFHWADDR, (void *)&ifr) == 0) {
if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
memcpy(macaddr, ifr.ifr_hwaddr.sa_data, 6);
return 0;
... etc ...
You've tagged the question "python". I don't know of an existing Python module to get this information. You could use ctypes to call the ioctl directly.
I posted Python find first network hop about trying to find the first hop and the more I thought about it, the easier it seemed like it would be a process the routing table in python. I'm not a programmer, I don't know what I'm doing. :p
This is what I came up with, the first issue I noticed is the loopback interface doesn't show up in the /proc/net/route file- so evaluating 127.0.0.0/8 will give you the default route... for my application, that doesn't matter.
Anything else major I'm overlooking? Is parsing ip route get <ip> still a better idea?
import re
import struct
import socket
'''
Read all the routes into a list. Most specific first.
# eth0 000219AC 04001EAC 0003 0 0 0 00FFFFFF ...
'''
def _RtTable():
_rt = []
rt_m = re.compile('^[a-z0-9]*\W([0-9A-F]{8})\W([0-9A-F]{8})[\W0-9]*([0-9A-F]{8})')
rt = open('/proc/net/route', 'r')
for line in rt.read().split('\n'):
if rt_m.match(line):
_rt.append(rt_m.findall(line)[0])
rt.close()
return _rt
'''
Create a temp ip (tip) that is the entered ip with the host
section striped off. Matching to routers in order,
the first match should be the most specific.
If we get 0.0.0.0 as the next hop, the network is likely(?)
directly attached- the entered IP is the next (only) hop
'''
def FindGw(ip):
int_ip = struct.unpack("I", socket.inet_aton(ip))[0]
for entry in _RtTable():
tip = int_ip & int(entry[2], 16)
if tip == int(entry[0], 16):
gw_s = socket.inet_ntoa(struct.pack("I", int(entry[1], 16)))
if gw_s == '0.0.0.0':
return ip
else:
return gw_s
if __name__ == '__main__':
import sys
print FindGw(sys.argv[1])
In the man page of proc file system it is given that.
/proc/net
various net pseudo-files, all of which give the status of some part of
the networking layer. These files contain ASCII structures and are,
there‐fore, readable with cat(1).
However, the standard netstat(8) suite provides much
cleaner access to these files.
Just rely on the tools designed for those purposes. Use netstat, traceroute or any other standard tool. Wrap those commands cleanly using subprocess module and get the information for what you are looking for.
With pyroute2.IPRoute, get the next hop on the way to some distant host, here — 8.8.8.8:
from pyroute2 import IPRoute
with IPRoute() as ipr:
print(ipr.route('get', dst='8.8.8.8'))
I want to get the IP of the default gateway (internal router IP) using Python. I'm really new to Python so not sure how this works.
I know you can get the IP of your machine with:
import socket
internal_ip = socket.gethostbyname(socket.gethostname())
print internal_ip
So I'm thinking it must be something similar?
On Windows, you should use WMI along with proper query to lookup properties of an object (e.g. network devices). The following Python code prints IPv4 and default gateway addresses on my Windows 7 machine:
Code:
import wmi
wmi_obj = wmi.WMI()
wmi_sql = "select IPAddress,DefaultIPGateway from Win32_NetworkAdapterConfiguration where IPEnabled=TRUE"
wmi_out = wmi_obj.query( wmi_sql )
for dev in wmi_out:
print "IPv4Address:", dev.IPAddress[0], "DefaultIPGateway:", dev.DefaultIPGateway[0]
Output:
IPv4Address: 192.168.0.2 DefaultIPGateway: 192.168.0.1
You can find more details and tricks of performing WMI operations on network devices on this page.
For Linux, PyNetInfo as suggested on this page would be a good approach. Although on Linux you can get around having to depend on an additional module by reading PROC entries among other import os; os.system(...) tricks.
I am running my code on multiple VPSes (with more than one IP, which are set up as aliases to the network interfaces) and I am trying to figure out a way such that my code acquires the IP addresses from the network interfaces on the fly and bind to it. Any ideas on how to do it in python without adding a 3rd party library ?
Edit I know about socket.gethostbyaddr(socket.gethostname()) and about the 3rd party package netifaces, but I am looking for something more elegant from the standard library ... and parsing the output of the ifconfig command is not something elegant :)
The IP addresses are assigned to your VPSes, no possibility to change them on the fly.
You have to open a SSH tunnel to or install a proxy on your VPSes.
I think a SSH tunnel would be the best way how to do it, and then use it as SOCKS5 proxy from Python.
This is how to get all IP addresses of the server the script is running on:
(this is as much elegant as possible and it only needs the standard library)
import socket
import fcntl
import struct
import array
def all_interfaces():
max_possible = 128 # arbitrary. raise if needed.
bytes = max_possible * 32
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
names = array.array('B', '\0' * bytes)
outbytes = struct.unpack('iL', fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack('iL', bytes, names.buffer_info()[0])
))[0]
namestr = names.tostring()
return [namestr[i:i+32].split('\0', 1)[0] for i in range(0, outbytes, 32)]
I need a cross platform method of determining the MAC address of a computer at run time. For windows the 'wmi' module can be used and the only method under Linux I could find was to run ifconfig and run a regex across its output. I don't like using a package that only works on one OS, and parsing the output of another program doesn't seem very elegant not to mention error prone.
Does anyone know a cross platform method (windows and linux) method to get the MAC address? If not, does anyone know any more elegant methods then those I listed above?
Python 2.5 includes an uuid implementation which (in at least one version) needs the mac address. You can import the mac finding function into your own code easily:
from uuid import getnode as get_mac
mac = get_mac()
The return value is the mac address as 48 bit integer.
The pure python solution for this problem under Linux to get the MAC for a specific local interface, originally posted as a comment by vishnubob and improved by on Ben Mackey in this activestate recipe
#!/usr/bin/python
import fcntl, socket, struct
def getHwAddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15]))
return ':'.join(['%02x' % ord(char) for char in info[18:24]])
print getHwAddr('eth0')
This is the Python 3 compatible code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import fcntl
import socket
import struct
def getHwAddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
return ':'.join('%02x' % b for b in info[18:24])
def main():
print(getHwAddr('enp0s8'))
if __name__ == "__main__":
main()
netifaces is a good module to use for getting the mac address (and other addresses). It's crossplatform and makes a bit more sense than using socket or uuid.
import netifaces
netifaces.interfaces()
# ['lo', 'eth0', 'tun2']
netifaces.ifaddresses('eth0')[netifaces.AF_LINK]
# [{'addr': '08:00:27:50:f2:51', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]
pypi location
Good Intro to netifaces
Sometimes we have more than one net interface.
A simple method to find out the mac address of a specific interface, is:
def getmac(interface):
try:
mac = open('/sys/class/net/'+interface+'/address').readline()
except:
mac = "00:00:00:00:00:00"
return mac[0:17]
to call the method is simple
myMAC = getmac("wlan0")
One other thing that you should note is that uuid.getnode() can fake the MAC addr by returning a random 48-bit number which may not be what you are expecting. Also, there's no explicit indication that the MAC address has been faked, but you could detect it by calling getnode() twice and seeing if the result varies. If the same value is returned by both calls, you have the MAC address, otherwise you are getting a faked address.
>>> print uuid.getnode.__doc__
Get the hardware address as a 48-bit positive integer.
The first time this runs, it may launch a separate program, which could
be quite slow. If all attempts to obtain the hardware address fail, we
choose a random 48-bit number with its eighth bit set to 1 as recommended
in RFC 4122.
Using my answer from here: https://stackoverflow.com/a/18031868/2362361
It would be important to know to which iface you want the MAC for since many can exist (bluetooth, several nics, etc.).
This does the job when you know the IP of the iface you need the MAC for, using netifaces (available in PyPI):
import netifaces as nif
def mac_for_ip(ip):
'Returns a list of MACs for interfaces that have given IP, returns None if not found'
for i in nif.interfaces():
addrs = nif.ifaddresses(i)
try:
if_mac = addrs[nif.AF_LINK][0]['addr']
if_ip = addrs[nif.AF_INET][0]['addr']
except IndexError, KeyError: #ignore ifaces that dont have MAC or IP
if_mac = if_ip = None
if if_ip == ip:
return if_mac
return None
Testing:
>>> mac_for_ip('169.254.90.191')
'2c:41:38:0a:94:8b'
You can do this with psutil which is cross-platform:
import psutil
nics = psutil.net_if_addrs()
print [j.address for j in nics[i] for i in nics if i!="lo" and j.family==17]
The cross-platform getmac package will work for this, if you don't mind taking on a dependency. It works with Python 2.7+ and 3.4+. It will try many different methods until either getting a address or returning None.
from getmac import get_mac_address
eth_mac = get_mac_address(interface="eth0")
win_mac = get_mac_address(interface="Ethernet 3")
ip_mac = get_mac_address(ip="192.168.0.1")
ip6_mac = get_mac_address(ip6="::1")
host_mac = get_mac_address(hostname="localhost")
updated_mac = get_mac_address(ip="10.0.0.1", network_request=True)
Disclaimer: I am the author of the package.
Update (Jan 14 2019): the package now only supports Python 2.7+ and 3.4+. You can still use an older version of the package if you need to work with an older Python (2.5, 2.6, 3.2, 3.3).
Note that you can build your own cross-platform library in python using conditional imports. e.g.
import platform
if platform.system() == 'Linux':
import LinuxMac
mac_address = LinuxMac.get_mac_address()
elif platform.system() == 'Windows':
# etc
This will allow you to use os.system calls or platform-specific libraries.
To get the eth0 interface MAC address,
import psutil
nics = psutil.net_if_addrs()['eth0']
for interface in nics:
if interface.family == 17:
print(interface.address)
nice actually return a dictionary within the dictionary it returns a list and within the list, it returns a staple. but nics['Ethernet'][0].address iteration solve the problem.
import psutil
nics = psutil.net_if_addrs()
mac_address = nics['Ethernet'][0].address
print(mac_address)
This cross-platform code does not 100% work on Windows. This works on Windows:
import psutil
print([(k, addr.address) for k, v in psutil.net_if_addrs().items() for addr in v if addr.family == -1])
Example:
[
('Local Area Connection', '01-23-45-67-89-0A'),
('Wireless Network Connection', '23-45-67-89-0A-BC'),
('Bluetooth Network Connection', '45-67-89-0A-BC-DE'),
('isatap.{01ABCDEF-0123-4567-890A-0123456789AB}', '00-00-00-00-00-00-00-01')
]
I dont know of a unified way, but heres something that you might find useful:
http://www.codeguru.com/Cpp/I-N/network/networkinformation/article.php/c5451
What I would do in this case would be to wrap these up into a function, and based on the OS it would run the proper command, parse as required and return only the MAC address formatted as you want. Its ofcourse all the same, except that you only have to do it once, and it looks cleaner from the main code.
Alternatively,
import uuid
mac_id=(':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff)
For Linux let me introduce a shell script that will show the mac address and allows to change it (MAC sniffing).
ifconfig eth0 | grep HWaddr |cut -dH -f2|cut -d\ -f2
00:26:6c:df:c3:95
Cut arguements may dffer (I am not an expert) try:
ifconfig etho | grep HWaddr
eth0 Link encap:Ethernet HWaddr 00:26:6c:df:c3:95
To change MAC we may do:
ifconfig eth0 down
ifconfig eth0 hw ether 00:80:48:BA:d1:30
ifconfig eth0 up
will change mac address to 00:80:48:BA:d1:30 (temporarily, will restore to actual one upon reboot).
For Linux you can retrieve the MAC address using a SIOCGIFHWADDR ioctl.
struct ifreq ifr;
uint8_t macaddr[6];
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
return -1;
strcpy(ifr.ifr_name, "eth0");
if (ioctl(s, SIOCGIFHWADDR, (void *)&ifr) == 0) {
if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
memcpy(macaddr, ifr.ifr_hwaddr.sa_data, 6);
return 0;
... etc ...
You've tagged the question "python". I don't know of an existing Python module to get this information. You could use ctypes to call the ioctl directly.