How to get the physical interface IP address from an interface

Johny_M picture Johny_M · Jun 5, 2011 · Viewed 52.3k times · Source

What I have done so far, using PyQt classes:

all_Addresses = QNetworkInterface.allAddresses()    #list-of-QHostAddress

for addr in all_Addresses:
    print(addr.toString())

Output:

172.16.0.186 - Virtual Interface IP address
192.168.10.2 - Physical interface IP address. I want this one.
127.0.0.1

Using socket:

import socket
print(socket.gethostbyname(socket.gethostname()))

Output:

172.16.0.186 - When openVPN is on
192.168.10.2 - When its off
  1. Is there a way to distinguish between them?
  2. Can this be done with ordinary Python, instead of using PyQt classes?
  3. How can I get the IPv6 address as well?

Answer

Mike Pennington picture Mike Pennington · Jun 6, 2011

You should use netifaces. It is designed to be cross-platform and contains specialised code for Windows together with a variety of generic versions that work on different UNIX/UNIX-like platforms.

As of netifaces version 0.10.0, Python3 is supported.

Usage Summary

>>> from netifaces import AF_INET, AF_INET6, AF_LINK, AF_PACKET, AF_BRIDGE
>>> import netifaces as ni
>>> ni.interfaces()
['lo', 'eth0', 'eth1', 'vboxnet0', 'dummy1']
>>>
>>> ni.ifaddresses('eth0')[AF_LINK]   # NOTE: AF_LINK is an alias for AF_PACKET
[{'broadcast': 'ff:ff:ff:ff:ff:ff', 'addr': '00:02:55:7b:b2:f6'}]
>>> ni.ifaddresses('eth0')[AF_INET]
[{'broadcast': '172.16.161.7', 'netmask': '255.255.255.248', 'addr': '172.16.161.6'}]
>>>
>>> # eth0 ipv4 interface address
>>> ni.ifaddresses('eth0')[AF_INET][0]['addr']
'172.16.161.6'
>>>>

Details

Windows Support:

No compiler required for most MS Windows installs. If you get warnings about installing MS Visual C++ for Windows, be very careful because you need to match the version of compiler used for your python with that used for the module.

Detailed example of netifaces data structures:

>>> import netifaces as ni
>>> ni.interfaces()
['lo', 'eth0', 'eth1', 'vboxnet0', 'dummy1']
>>> ni.ifaddresses('eth0')
{
    17: [
        {
            'broadcast': 'ff:ff:ff:ff:ff:ff',
            'addr': '00:02:55:7b:b2:f6'
        }
    ],
    2: [
        {
            'broadcast': '172.16.161.7',
            'netmask': '255.255.255.248',
            'addr': '172.16.161.6'
        }
    ],
    10: [
        {
            'netmask': 'ffff:ffff:ffff:ffff::',
            'addr': 'fe80::202:55ff:fe7b:b2f6%eth0'
        }
    ]
}
>>> 
>>> print(ni.ifaddresses.__doc__)
Obtain information about the specified network interface.

Returns a dict whose keys are equal to the address family constants,
e.g. netifaces.AF_INET, and whose values are a list of addresses in
that family that are attached to the network interface.
>>>
>>> # for the IPv4 address of eth0
>>> ni.ifaddresses('eth0')[2][0]['addr']
'172.16.161.6'

The numbers used to index protocols are from /usr/include/linux/socket.h (in Linux)... EDIT: my 3.2 kernel has them here: /usr/src/linux-headers-3.2.0-4-common/include/linux/socket.h

#define AF_INET         2       /* Internet IP Protocol         */
#define AF_INET6        10      /* IP version 6                 */
#define AF_PACKET       17      /* Packet family                */

The good news is that you don't have to remember all those header constants, they are included with netifaces:

>>> from netifaces import AF_INET, AF_INET6, AF_LINK, AF_PACKET, AF_BRIDGE
>>> import netifaces as ni