Created
December 10, 2015 23:17
-
-
Save AxelVoitier/8d74496adb169df28d8b to your computer and use it in GitHub Desktop.
Test getting a structure from libc using function getifaddrs (original code from zeromq/pyre)
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
from ctypes import c_char, c_char_p | |
from ctypes import c_uint, c_uint8, c_uint16, c_uint32 | |
from ctypes import c_short, c_ushort | |
from ctypes import c_void_p, pointer | |
from ctypes import CDLL, Structure, Union | |
from sys import platform | |
if platform.startswith("win") and sys.version.startswith("2"): | |
import win_inet_pton | |
from socket import AF_INET, AF_INET6, inet_ntop | |
try: | |
from socket import AF_PACKET | |
except ImportError: | |
AF_PACKET = -1 | |
if platform.startswith("darwin") or platform.startswith("freebsd"): | |
AF_LINK = 18 | |
IFT_ETHER = 0x6 | |
else: | |
AF_LINK = -1 | |
IFT_ETHER = -1 | |
def get_ifaddrs(): | |
""" | |
A method for retrieving info of the network interfaces. | |
Returns a nested dictionary containing everything it found. | |
{ | |
ifname: | |
{ | |
familynr: | |
{ | |
addr: | |
netmask: | |
etc... | |
Found this at http://pastebin.com/wxjai3Mw with some modification to | |
make it work on OSX. | |
""" | |
if platform.startswith("win"): | |
return get_win_ifaddrs() | |
# getifaddr structs | |
class ifa_ifu_u(Union): | |
_fields_ = [ | |
("ifu_broadaddr", c_void_p), | |
("ifu_dstaddr", c_void_p) | |
] | |
class ifaddrs(Structure): | |
_fields_ = [ | |
("ifa_next", c_void_p), | |
("ifa_name", c_char_p), | |
("ifa_flags", c_uint), | |
("ifa_addr", c_void_p), | |
("ifa_netmask", c_void_p), | |
("ifa_ifu", ifa_ifu_u), | |
("ifa_data", c_void_p) | |
] | |
# AF_UNKNOWN / generic | |
if platform.startswith("darwin") or platform.startswith("freebsd"): | |
class sockaddr(Structure): | |
_fields_ = [ | |
("sa_len", c_uint8), | |
("sa_family", c_uint8), | |
("sa_data", (c_uint8 * 14)) | |
] | |
else: | |
class sockaddr(Structure): | |
_fields_ = [ | |
("sa_family", c_uint16), | |
("sa_data", (c_uint8 * 14)) | |
] | |
# AF_INET / IPv4 | |
class in_addr(Union): | |
_fields_ = [ | |
("s_addr", c_uint32), | |
] | |
if platform.startswith("darwin") or platform.startswith("freebsd"): | |
class sockaddr_in(Structure): | |
_fields_ = [ | |
("sin_len", c_uint8), | |
("sin_family", c_uint8), | |
("sin_port", c_ushort), | |
("sin_addr", in_addr), | |
("sin_zero", (c_char * 8)) # padding | |
] | |
else: | |
class sockaddr_in(Structure): | |
_fields_ = [ | |
("sin_family", c_short), | |
("sin_port", c_ushort), | |
("sin_addr", in_addr), | |
("sin_zero", (c_char * 8)) # padding | |
] | |
# AF_INET6 / IPv6 | |
class in6_u(Union): | |
_fields_ = [ | |
("u6_addr8", (c_uint8 * 16)), | |
("u6_addr16", (c_uint16 * 8)), | |
("u6_addr32", (c_uint32 * 4)) | |
] | |
class in6_addr(Union): | |
_fields_ = [ | |
("in6_u", in6_u), | |
] | |
if platform.startswith("darwin") or platform.startswith("freebsd"): | |
class sockaddr_in6(Structure): | |
_fields_ = [ | |
("sin6_len", c_uint8), | |
("sin6_family", c_uint8), | |
("sin6_port", c_ushort), | |
("sin6_flowinfo", c_uint32), | |
("sin6_addr", in6_addr), | |
("sin6_scope_id", c_uint32), | |
] | |
else: | |
class sockaddr_in6(Structure): | |
_fields_ = [ | |
("sin6_family", c_short), | |
("sin6_port", c_ushort), | |
("sin6_flowinfo", c_uint32), | |
("sin6_addr", in6_addr), | |
("sin6_scope_id", c_uint32), | |
] | |
# AF_PACKET / Linux | |
class sockaddr_ll(Structure): | |
_fields_ = [ | |
("sll_family", c_uint16), | |
("sll_protocol", c_uint16), | |
("sll_ifindex", c_uint32), | |
("sll_hatype", c_uint16), | |
("sll_pktype", c_uint8), | |
("sll_halen", c_uint8), | |
("sll_addr", (c_uint8 * 8)) | |
] | |
# AF_LINK / BSD|OSX | |
class sockaddr_dl(Structure): | |
_fields_ = [ | |
("sdl_len", c_uint8), | |
("sdl_family", c_uint8), | |
("sdl_index", c_uint16), | |
("sdl_type", c_uint8), | |
("sdl_nlen", c_uint8), | |
("sdl_alen", c_uint8), | |
("sdl_slen", c_uint8), | |
("sdl_data", (c_uint8 * 46)) | |
] | |
if platform.startswith("darwin"): | |
libc = CDLL("libSystem.dylib") | |
elif platform.startswith("freebsd"): | |
libc = CDLL("libc.so") | |
else: | |
libc = CDLL("libc.so.6") | |
ptr = c_void_p(None) | |
result = libc.getifaddrs(pointer(ptr)) | |
if result: | |
return None | |
ifa = ifaddrs.from_address(ptr.value) | |
result = [] | |
while ifa: | |
# Python 2 gives us a string, Python 3 an array of bytes | |
if type(ifa.ifa_name) is str: | |
name = ifa.ifa_name | |
else: | |
name = ifa.ifa_name.decode() | |
if ifa.ifa_addr: | |
sa = sockaddr.from_address(ifa.ifa_addr) | |
data = {} | |
if sa.sa_family == AF_INET: | |
if ifa.ifa_addr is not None: | |
si = sockaddr_in.from_address(ifa.ifa_addr) | |
data['addr'] = inet_ntop(AF_INET, si.sin_addr) | |
if ifa.ifa_netmask is not None: | |
si = sockaddr_in.from_address(ifa.ifa_netmask) | |
data['netmask'] = inet_ntop(AF_INET, si.sin_addr) | |
# check if a valid broadcast address is set and retrieve it | |
# 0x2 == IFF_BROADCAST | |
if ifa.ifa_flags & 0x2: | |
si = sockaddr_in.from_address(ifa.ifa_ifu.ifu_broadaddr) | |
data['broadcast'] = inet_ntop(AF_INET, si.sin_addr) | |
if sa.sa_family == AF_INET6: | |
if ifa.ifa_addr is not None: | |
si = sockaddr_in6.from_address(ifa.ifa_addr) | |
data['addr'] = inet_ntop(AF_INET6, si.sin6_addr) | |
if data['addr'].startswith('fe80:'): | |
data['scope'] = si.sin6_scope_id | |
if ifa.ifa_netmask is not None: | |
si = sockaddr_in6.from_address(ifa.ifa_netmask) | |
data['netmask'] = inet_ntop(AF_INET6, si.sin6_addr) | |
if sa.sa_family == AF_PACKET: | |
if ifa.ifa_addr is not None: | |
si = sockaddr_ll.from_address(ifa.ifa_addr) | |
addr = "" | |
total = 0 | |
for i in range(si.sll_halen): | |
total += si.sll_addr[i] | |
addr += "%02x:" % si.sll_addr[i] | |
addr = addr[:-1] | |
if total > 0: | |
data['addr'] = addr | |
if sa.sa_family == AF_LINK: | |
dl = sockaddr_dl.from_address(ifa.ifa_addr) | |
if dl.sdl_type == IFT_ETHER: | |
addr = "" | |
for i in range(dl.sdl_alen): | |
addr += "%02x:" % dl.sdl_data[dl.sdl_nlen + i] | |
addr = addr[:-1] | |
data['addr'] = addr | |
if len(data) > 0: | |
iface = {} | |
for interface in result: | |
if name in interface.keys(): | |
iface = interface | |
break | |
if iface: | |
iface[name][sa.sa_family] = data | |
else: | |
iface[name] = { sa.sa_family : data } | |
result.append(iface) | |
if ifa.ifa_next: | |
ifa = ifaddrs.from_address(ifa.ifa_next) | |
else: | |
break | |
libc.freeifaddrs(ptr) | |
return result | |
from pprint import pprint as pp | |
pp(get_ifaddrs()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment