-
-
Save chadmiller/5157850 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# Based on https://gist.github.com/provegard/1536682, which was | |
# Based on getifaddrs.py from pydlnadms [http://code.google.com/p/pydlnadms/]. | |
# Only tested on Linux! | |
from socket import AF_INET, AF_INET6, inet_ntop | |
from ctypes import ( | |
Structure, Union, POINTER, | |
pointer, get_errno, cast, | |
c_ushort, c_byte, c_void_p, c_char_p, c_uint, c_int, c_uint16, c_uint32 | |
) | |
import ctypes.util | |
import ctypes | |
class struct_sockaddr(Structure): | |
_fields_ = [ | |
('sa_family', c_ushort), | |
('sa_data', c_byte * 14),] | |
class struct_sockaddr_in(Structure): | |
_fields_ = [ | |
('sin_family', c_ushort), | |
('sin_port', c_uint16), | |
('sin_addr', c_byte * 4)] | |
class struct_sockaddr_in6(Structure): | |
_fields_ = [ | |
('sin6_family', c_ushort), | |
('sin6_port', c_uint16), | |
('sin6_flowinfo', c_uint32), | |
('sin6_addr', c_byte * 16), | |
('sin6_scope_id', c_uint32)] | |
class union_ifa_ifu(Union): | |
_fields_ = [ | |
('ifu_broadaddr', POINTER(struct_sockaddr)), | |
('ifu_dstaddr', POINTER(struct_sockaddr)),] | |
class struct_ifaddrs(Structure): | |
pass | |
struct_ifaddrs._fields_ = [ | |
('ifa_next', POINTER(struct_ifaddrs)), | |
('ifa_name', c_char_p), | |
('ifa_flags', c_uint), | |
('ifa_addr', POINTER(struct_sockaddr)), | |
('ifa_netmask', POINTER(struct_sockaddr)), | |
('ifa_ifu', union_ifa_ifu), | |
('ifa_data', c_void_p),] | |
libc = ctypes.CDLL(ctypes.util.find_library('c')) | |
def ifap_iter(ifap): | |
ifa = ifap.contents | |
while True: | |
yield ifa | |
if not ifa.ifa_next: | |
break | |
ifa = ifa.ifa_next.contents | |
def getfamaddr(sa): | |
family = sa.sa_family | |
addr = None | |
if family == AF_INET: | |
sa = cast(pointer(sa), POINTER(struct_sockaddr_in)).contents | |
addr = inet_ntop(family, sa.sin_addr) | |
elif family == AF_INET6: | |
sa = cast(pointer(sa), POINTER(struct_sockaddr_in6)).contents | |
addr = inet_ntop(family, sa.sin6_addr) | |
return family, addr | |
class NetworkInterface(object): | |
def __init__(self, name): | |
self.name = name | |
self.index = libc.if_nametoindex(name) | |
self.addresses = {} | |
def __str__(self): | |
return "%s [index=%d, IPv4=%s, IPv6=%s]" % ( | |
self.name, self.index, | |
self.addresses.get(AF_INET), | |
self.addresses.get(AF_INET6)) | |
def get_network_interfaces(): | |
ifap = POINTER(struct_ifaddrs)() | |
result = libc.getifaddrs(pointer(ifap)) | |
if result != 0: | |
raise OSError(get_errno()) | |
del result | |
try: | |
retval = {} | |
for ifa in ifap_iter(ifap): | |
name = ifa.ifa_name.decode("UTF-8") | |
i = retval.get(name) | |
if not i: | |
i = retval[name] = NetworkInterface(name) | |
family, addr = getfamaddr(ifa.ifa_addr.contents) | |
if addr: | |
if family not in i.addresses: | |
i.addresses[family] = list() | |
i.addresses[family].append(addr) | |
return retval.values() | |
finally: | |
libc.freeifaddrs(ifap) | |
if __name__ == '__main__': | |
print([str(ni) for ni in get_network_interfaces()]) |
To make ctypes.get_errno()
work, the call to ctypes.CDLL()
must enable errno
handling via ctypes, like this:
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
Also, it is a good idea to check the return value of if_nametoindex
:
class NetworkInterfaces:
def __init__(self, name):
self.name = name
self.index = libc.if_nametoindex(name.encode('utf-8'))
if self.index == 0:
raise OSError(ctypes.get_errno())
self.addresses = {}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've created a fork that adds support for OS X, in case this of interest. Apparently it's impossible to create a pull request on GitHub Gist so am just commenting here.