| Main |

Getting network addresses from Python

It’s been irritating me for ages that it isn’t possible to straightforwardly get the network address(es) of the machine you’re running on from a Python program.

Well, if you’re running Mac OS X, or probably some of the various other UNIX-like systems, now you can (here’s the source code).

Here's a quick example of how to use it:

>>> import netifaces
>>> netifaces.interfaces()
['lo0', 'gif0', 'stf0', 'en0', 'en1', 'fw0']
>>> netifaces.ifaddresses('en0')
{18: [{'addr': '00:12:34:56:78:9a'}], 2: [{'broadcast':
 '10.255.255.255', 'netmask': '255.0.0.0', 'addr': '10.16.1.4'}],
 30: [{'netmask': 'ffff:ffff:ffff:ffff::', 'addr':
 'fe80::123:4567:89ab:cdef%en0'}]}

Note that presently this has only been tested on Mac OS X (the SIOCGIFxxx ioctl()s have been tested, apart from SIOCGIFHWADDR, which doesn’t exist on OS X).

Note also that it won’t work on Windows. You’d need to use the Windows Sockets functions to get similar functionality on Windows, I think.

Update (2007/03/18)

It works on Windows now too! There are also pre-built binary versions for Windows (Python 2.4, Python 2.5), and also Mac OS X. But do yourself a favour and use easy_install:

easy_install netifaces

(You can get easy_install from the PEAK Developers’ Center if you don’t have it already.)

The Windows code can currently only return IPv4 addresses and MAC addresses; also, since Windows doesn’t define AF_LINK, I’ve defined it in netifaces for compatibility.

Update (2007/03/20)

I’ve updated the code so that it builds on Windows with MSVC as well as with GCC. Also, I’ve added support for Linux (which lacks the sa_len member in struct sockaddr, making getifaddrs() somewhat harder to use sensibly).

I’ve also build some more binaries:

Python 2.4/Mac OS X 10.4

Python 2.4/Win32

Python 2.5/Win32

Python 2.4/x86 Linux

Python 2.5/x86 Linux

Update (2008/02/24)

Christian Kauhaus pointed out that the PPP interface on Linux sometimes results in a getifaddrs() record with no address, which causes a crash. Also, Python's egg selection mechanism meant that the Linux binaries were a nuisance for those with older versions of Linux, so I've removed them.

Trackbacks

TrackBack URL for this entry:
http://alastairs-place.net/movabletype/mt-tb.cgi/131.

Comments

Thanks for the library! It's very useful.

I remarked that when using the library on Linux with a 64bit kernel, the library segfaults. That's either with a 32bit or 64bit libc. I'll try to debug a little when I have time to see where it crashes.

Yes, please do. I don’t have anything running 64-bit Linux (I suppose some of the Macs I have probably could, but I’d rather not have to install it as I pretty much exclusively use Mac OS X these days, and repartitioning and installing Linux just to test this is something I’d prefer to avoid if possible).

If you work out what’s wrong, drop me a line and I’ll merge whatever fixes need doing into the code.

Anyway, I’m pleased to hear that the library is useful (I’ve had a few other such remarks, mostly by e-mail).

Oops... I properly compiled in 64 bits the library and it works now. Please ignore my previous comment :-)

Dear Alastair,

I don't understand the purpose of this dictionary's organization. Lets take a sample code:
netifaces.ifaddresses('eth0')
{17: [{'broadcast': 'ff:ff:ff:ff:ff:ff', 'addr': '00:1b:fc:ed:4a:71'}], 2: [{'broadcast': '192.168.1.255', 'netmask': '255.255.255.0', 'addr': '192.168.1.34'}], 10: [{'netmask': 'ffff:ffff:ffff:ffff::', 'addr': 'fe80::21b:fcff:feed:4a71%eth0'}]}
One has here a dictionary with number keys?? What do these numbers mean? I see 17,2,10.

I feel ridiculous accessing a simple information like IP adress:
netifaces.ifaddresses('eth0')[2][0]['addr']
'192.168.1.34'
so i thought.. I can iterate over the ifaces.interfaces() list and mantain [2]. but looks like is not going to work:
netifaces.ifaddresses('wlan0')[2][0]['addr']
KeyError: 2

Please, can you point me in a way of using your module in an usable way?

OK. The outermost dictionary maps address types to addresses. The actual values of the address types are system specific, but netifaces provides symbols to help with that.

So netifaces.ifaddresses('en0')[netifaces.AF_INET] will give you the Internet family addresses for the interface en0.

Note that I said addresses and not just address. There can, of course, be more than one, so the above expression returns an array.

Also note that not all address families have broadcast an netmask components.

Alastair, nice work on this module. Seems badly needed. In your last comment you mentioned that there are a number of symbols (AF_INET etc) available to index the returned data. This may seem silly but how do I figure what these symbols mean and what they are without having a priori knowledge of them. Cheers, Mats

@Mats:
You can't, not in general anyway, because they're system specific.

AF_INET and AF_INET6 are standard (and mean IPv4 and IPv6 respectively); AF_UNIX works on Unix and POSIX platforms and refers to a socket in the filesystem; and AF_LINK will give you the network address, the format of which is dependent on the type of interface. The first three are standardised by POSIX; AF_LINK isn't standard, but netifaces provides it where possible (even if the underlying system doesn't have it).

Other than those, your best source of information is either the docs for the operating system you're using, or the header file (or wherever the platform declares its address family constants). Usually there are comments that tell you what the address family is for. There are quite a few other common address family constants, but most of them are only useful in special circumstances, e.g. if you happen to have a machine with an X.25 or ATM link or something.

netifaces does its best to present addresses in a reasonable way no matter what; it uses getnameinfo() where possible to format addresses, and if it can't do that then it falls back to formatting addresses the way you'd expect for e.g. an Ethernet MAC address---i.e. hexadecimal bytes separated by ‘:’s---which seemed like a reasonable solution.

Carlos Fontes said,
"I feel ridiculous accessing a simple information like IP adress:
netifaces.ifaddresses('eth0')[2][0]['addr']
'192.168.1.34'"

Can you break down what he did to the dictionary? What do [2][0]['addr']
each represent?

Thanks.

@Coward:

OK, so netifaces.ifaddresses('eth0') returns a dictionary indexed by address family (e.g. AF_INET for IPv4, AF_INET6 for IPv6). The first index (the [2]) therefore is the address family, but please don’t write it as a numeric constant like that, because the values of the address family constants vary from system to system. The commenter should have written [netifaces.AF_INET] instead. It so happens that AF_INET is very likely to be 2, but that is not necessarily true everywhere.

So that brings us to the [0] part. Why, you ask, is this necessary? Well, because an interface can have multiple addresses of the same family. For instance, you might have a machine with only one interface that responds to 192.168.1.5 and 10.16.74.8. As a result, netifaces.ifaddresses('eth0')[netifaces.AF_INET] returns an array, rather than a single element.

The final part, ['addr'], is just to select the address rather than the netmask, broadcast address or anything else. I suppose I could have provided constants for the various things that can be used here, but IMO it wouldn’t make things any more readable and it would make it harder to read the output of netifaces.ifaddresses('eth0') at the prompt.

Perhaps it would help to give some idea of what the data looks like, so here’s an example (reformatted slightly and with numeric constants replaced to emphasise the point that you should be using the AF_ constants from the netifaces module):

>>> netifaces.ifaddresses('en0')
{ netifaces.AF_LINK: [{ 'addr': '00:12:34:56:78:90' }],
  netifaces.AF_INET: [{ 'broadcast': '10.255.255.255',
                          'netmask': '255.0.0.0',
                             'addr': '10.16.74.8'},
                      { 'broadcast': '192.168.1.255',
                          'netmask': '255.255.255.0',
                             'addr': '196.168.1.5' }],
  netifaces.AF_INET6: [{ 'netmask': 'ffff:ffff:ffff:ffff::',
                            'addr': 'fe80::123:4567:89ab:cdef%en0' }]}

Note also that the primary Ethernet adapter is not necessarily called ‘eth0’. Different systems use different naming conventions; Windows is probably the most different (it uses GUIDs). You should use the list of interfaces available from netifaces.interfaces(); if you need to find the “primary” adapter, you can use some heuristics to guess which one this is, but in general you would have to ask the user anyway because what you are really asking in such cases is which interface they want your program to use.

Hi folks

this is indeed a real nice utility library but as it did not work
on Solaris platform I wrote a small patch to cope with this "lack". It can be downloaded at
http://olivierbourdon.homedns.org/OpenSource/netifaces-0.4-solaris.patch

so the procedure is:
1) get the sources
wget http://alastairs-place.net/2007/03/netifaces/netifaces-0.4.tar.gz
2) extract them
tar zxf netifaces-0.4.tar.gz
3) get the patch
wget http://olivierbourdon.homedns.org/OpenSource/netifaces-0.4-solaris.patch
4) patch the sources
cd netifaces-0.4
patch -p1 < ../netifaces-0.4-solaris.patch
5) rebuild & install
python setup.py build
sudo python setup.py install

This has been tested sucessfully on:
Solaris 10 update 6 with Sun Compiler
OpenSolaris build 98 with gcc (from /usr/sfw/bin)

Thanks for the good work

Hello everyone

again I made a patch for this usefull piece of code to run on Solaris with logical interfaces
defined (like for zones or other)
you can patch either from my previous solaris patch using
wget http://olivierbourdon.homedns.org/OpenSource/netifaces-0.4-logical-interfaces.patch
or from the original delivery using
wget http://olivierbourdon.homedns.org/OpenSource/netifaces-0.4-logical-interfaces-solaris.patch

using the following script on my solaris box with one zone defined gives
cat > test.py import netifaces
itfs = netifaces.interfaces()
print itfs
for i in itfs:
print i,netifaces.ifaddresses(i)
_EOF

# python test.py
['lo0', 'lo0:1', 'pcn0', 'pcn0:1']
lo0 {2: [{'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]}
lo0:1 {2: [{'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]}
pcn0 {2: [{'broadcast': 'X.Y.Z.255', 'netmask': '255.255.255.0', 'addr': 'X.Y.Z.141'}]}
pcn0:1 {2: [{'broadcast': 'X.Y.Z.255', 'netmask': '255.255.255.0', 'addr': 'X.Y.Z.64'}]}

# ifconfig -a
lo0: flags=2001000849 mtu 8232 index 1
inet 127.0.0.1 netmask ff000000
lo0:1: flags=2001000849 mtu 8232 index 1
zone z1
inet 127.0.0.1 netmask ff000000
pcn0: flags=1004843 mtu 1500 index 2
inet X.Y.Z.141 netmask ffffff00 broadcast X.Y.Z.255
ether 0:c:29:95:80:48
pcn0:1: flags=1000843 mtu 1500 index 2
zone z1
inet X.Y.Z.64 netmask ffffff00 broadcast 129.157.211.255

I got my "inspiration" from the very usefull article:
http://www.unix.com/sun-solaris/72871-getting-interface-names-using-code-2.html
and tried to keep the code as generic as possible

Hope this will be usefull to python addicts ;-)

Your package has been uploaded to Debian:
python-netifaces

Thanks for your work.

(For anyone watching this page for changes, I'll look into merging in some of the Solaris related changes at some point soon.)

Post a comment

If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thank-you for your patience.

(Your e-mail address will not be displayed or included in any pages served on this site; nor will you get any spam as a result.)

A live preview of your comment will be displayed below. It should refresh automatically when you stop typing, but if not then the “Preview” button above will update it.

Live Comment Preview