pythonpython-3.xpyqtip-address

How to get the physical interface IP address from an interface


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?

Solution

  • 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