45

I have multiple Network Interface Cards on my computer, each with its own IP address.

When I use gethostbyname(gethostname()) from Python's (built-in) socket module, it will only return one of them. How do I get the others?

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Wilson F
  • 1,250
  • 1
  • 20
  • 37
  • 1
    Could you change "address" to "addresses" in the title, since it better reflects the question (first glance; mutiple NICs -> multiple IP addresses). – Rob Nov 07 '08 at 00:33
  • 3
    I was just going to have the comment be "Done.", but it was too short and needed to get up to 10 chars. – Harley Holcombe Nov 08 '08 at 11:11
  • Relying on hostname resolution is misguided. You don't need hostname to list the IP-addresses, the concepts are only remotely related. – Mikhail T. Dec 14 '18 at 01:04

14 Answers14

64

Use the netifaces module. Because networking is complex, using netifaces can be a little tricky, but here's how to do what you want:

>>> import netifaces
>>> netifaces.interfaces()
['lo', 'eth0']
>>> netifaces.ifaddresses('eth0')
{17: [{'broadcast': 'ff:ff:ff:ff:ff:ff', 'addr': '00:11:2f:32:63:45'}], 2: [{'broadcast': '10.0.0.255', 'netmask': '255.255.255.0', 'addr': '10.0.0.2'}], 10: [{'netmask': 'ffff:ffff:ffff:ffff::', 'addr': 'fe80::211:2fff:fe32:6345%eth0'}]}
>>> for interface in netifaces.interfaces():
...   print netifaces.ifaddresses(interface)[netifaces.AF_INET]
...
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]
[{'broadcast': '10.0.0.255', 'netmask': '255.255.255.0', 'addr': '10.0.0.2'}]
>>> for interface in netifaces.interfaces():
...   for link in netifaces.ifaddresses(interface)[netifaces.AF_INET]:
...     print link['addr']
...
127.0.0.1
10.0.0.2

This can be made a little more readable like this:

from netifaces import interfaces, ifaddresses, AF_INET

def ip4_addresses():
    ip_list = []
    for interface in interfaces():
        for link in ifaddresses(interface)[AF_INET]:
            ip_list.append(link['addr'])
    return ip_list

If you want IPv6 addresses, use AF_INET6 instead of AF_INET. If you're wondering why netifaces uses lists and dictionaries all over the place, it's because a single computer can have multiple NICs, and each NIC can have multiple addresses, and each address has its own set of options.

Eric Blum
  • 744
  • 12
  • 29
Harley Holcombe
  • 175,848
  • 15
  • 70
  • 63
  • PS. This answer is fully tested, unlike the other highly upvoted one I gave. That (very wrong) answer has been deleted. – Harley Holcombe Nov 08 '08 at 11:44
  • 1
    Just a short note: on my local machine, the netifaces.ifaddresses may return an empty dict in some cases, so the code will fail in that case when trying to access the AF_INET key. Minor fix, but otherwise great answer. – zvikico May 30 '12 at 08:53
  • 3
    I've changed it to `for link in ifaddresses(interface).get(AF_INET, ())`. I also ignore some interfaces like 'lo0'. – Julio Batista Silva Oct 04 '15 at 06:36
  • 3
    @HarleyHolcombe - The ip4_addresses() function throws KeyError if a NIC doesn't have an IPv4 address and should use the dict.get(key) form both for AF_INET and 'addr'. I submitted an edit to your answer with the fixes, but my edit was rejected (???). – Vijay Varadan Feb 14 '17 at 18:40
22
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Nakilon
  • 34,866
  • 14
  • 107
  • 142
  • Works without additional modules; Python 2.7, Ubuntu Linux. – pnovotnak Sep 30 '14 at 20:40
  • 7
    This just lists the address of the interface that is attached to the outside world, not all interfaces. – Paul Hoffman Nov 16 '14 at 03:26
  • 1
    This uses nameresolution -- expect it to match the network interface. That's not an unreasonable expectation, but it might fail. What the asker wants -- even he does not realize it -- is the equivalent of output from `ifconfig`. – Mikhail T. Dec 14 '18 at 00:24
  • 1
    Use `{i[4][0] for ... , None)}` to get a unique set of addresses. If you need a list, wrap it in a `list(...)` call. – grandchild Oct 10 '19 at 18:13
  • 1
    I wish I could upvote this, but as @PaulHoffman points out, it doesn't return the IP for all interfaces. I need a standard library method of doing so, or otherwise skip to parsing the output of `ip a` and call it a day. –  Jul 23 '20 at 18:39
  • I understand you guys, but it "worked for me" in 2013 and I'm not working with Python since then. – Nakilon Jul 24 '20 at 14:04
15

Just for completeness, another option would be to use psutil.

tldr;

import socket
import psutil

def get_ip_addresses(family):
    for interface, snics in psutil.net_if_addrs().items():
        for snic in snics:
            if snic.family == family:
                yield (interface, snic.address)

ipv4s = list(get_ip_addresses(socket.AF_INET))
ipv6s = list(get_ip_addresses(socket.AF_INET6))

Explanation

The function you need is net_if_addrs. I.e.:

import psutil
psutil.net_if_addrs()

Which results in something like this (Python 3):

{'br-ae4880aa80cf': [snic(family=<AddressFamily.AF_INET: 2>, address='172.18.0.1', netmask='255.255.0.0', broadcast='172.18.0.1', ptp=None),
                     snic(family=<AddressFamily.AF_PACKET: 17>, address='02:42:e5:ae:39:94', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'docker0': [snic(family=<AddressFamily.AF_INET: 2>, address='172.17.0.1', netmask='255.255.0.0', broadcast='172.17.0.1', ptp=None),
             snic(family=<AddressFamily.AF_PACKET: 17>, address='02:42:38:d2:4d:77', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'eno1': [snic(family=<AddressFamily.AF_PACKET: 17>, address='54:be:f7:0b:cf:a9', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
        snic(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
 'wlp2s0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.4', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
            snic(family=<AddressFamily.AF_PACKET: 17>, address='00:21:27:ee:d6:03', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}

(Python 2):

{'br-ae4880aa80cf': [snic(family=2, address='172.18.0.1', netmask='255.255.0.0', broadcast='172.18.0.1', ptp=None),
                     snic(family=17, address='02:42:e5:ae:39:94', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'docker0': [snic(family=2, address='172.17.0.1', netmask='255.255.0.0', broadcast='172.17.0.1', ptp=None),
             snic(family=17, address='02:42:38:d2:4d:77', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'eno1': [snic(family=17, address='54:be:f7:0b:cf:a9', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'lo': [snic(family=2, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
        snic(family=17, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
 'wlp2s0': [snic(family=2, address='192.168.1.4', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
            snic(family=17, address='00:21:27:ee:d6:03', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}

Note: Since you can have more than one address of the same family associated with each interface, the dict values are lists.

Each snic is a namedtuple which includes 5 fields:

  • family: the address family, either AF_INET, AF_INET6 or psutil.AF_LINK, which refers to a MAC address.
  • address: the primary NIC address (always set).
  • netmask: the netmask address (may be None).
  • broadcast: the broadcast address (may be None).
  • ptp: stands for “point to point”; it’s the destination address on a point to point interface (typically a VPN). broadcast and ptp are mutually exclusive (may be None).
pmav99
  • 1,909
  • 2
  • 20
  • 27
  • 1
    Since netifaces is not available on the conda distribution on raspberry pi, I really like this method. Cross platform (tested on windows and raspberry pi raspbian 8). – NoOneKnowsImaDog Apr 24 '18 at 13:24
7

All addresses in one line with the help of the netifaces module:

[netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for iface in netifaces.interfaces() if netifaces.AF_INET in netifaces.ifaddresses(iface)]
Elemag
  • 96
  • 2
  • 4
  • Thanks @Elemag. This works on Python interpreter but not when I save it it into .py https://stackoverflow.com/questions/49195864/how-to-get-all-ip-addresses-using-python-windows –  Mar 09 '18 at 16:31
  • 1
    @Sabrina You must do something with the result, such as printing it. The code executes but it does not get visualised in any way – Elemag Mar 13 '18 at 06:45
6

https://docs.python.org/3.4/library/socket.html#socket.if_nameindex

socket.if_nameindex()

Return a list of network interface information (index int, name string) tuples. OSError if the system call fails.

Availability: Unix.

New in version 3.3.


made this code that is runable on Python 3.4, UNIX / Linux

#!/env/python3.4
import socket
import fcntl
import struct

def active_nic_addresses():
    """
    Return a list of IPv4 addresses that are active on the computer.
    """

    addresses = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1]

    return addresses

def get_ip_address( NICname ):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915,  # SIOCGIFADDR
        struct.pack('256s', NICname[:15].encode("UTF-8"))
    )[20:24])


def nic_info():
    """
    Return a list with tuples containing NIC and IPv4
    """
    nic = []

    for ix in socket.if_nameindex():
        name = ix[1]
        ip = get_ip_address( name )

        nic.append( (name, ip) )

    return nic

if __name__ == "__main__":

    print( active_nic_addresses() )
    print( nic_info() )

Will print something like:

['192.168.0.2']
[('lo', '127.0.0.1'), ('enp3s0', '192.168.0.2')]
The Demz
  • 7,066
  • 5
  • 39
  • 43
2

As this thread indicates, there is a lot of ways to acchive the same result, my suggested way is to leverage the build-in family filter in getaddrinfo() and parse the standardised tuple like so:

from socket import getaddrinfo, AF_INET, gethostname

for ip in getaddrinfo(host=gethostname(), port=None, family=AF_INET):   
    print(ip[4][0])

Example output:

192.168.55.1
192.168.170.234
chjortlund
  • 3,613
  • 2
  • 31
  • 30
1

It's linux only, but there's a very simple recipe here http://code.activestate.com/recipes/439094/

It probably uses similar code to the netifaces package mentioned in another answer (but current version linked here)

The socket.getaddrinfo() doesn't actually return the bound ip address for the device. If your hosts file contains a line with "127.0.1.1 yourhost.example.com yourhost", which is a common configuration, getaddrinfo is only going to return 127.0.1.1.

JimB
  • 104,193
  • 13
  • 262
  • 255
1

This snippet will give a list of all available IPV4 addresses in the system.

import itertools
from netifaces import interfaces, ifaddresses, AF_INET

links = filter(None, (ifaddresses(x).get(AF_INET) for x in interfaces()))
links = itertools.chain(*links)
ip_addresses = [x['addr'] for x in links]
Sandeep
  • 28,307
  • 3
  • 32
  • 24
0

Here is a routine for finding all IPv4 and IPv6 interfaces. As a previous poster pointed out, socket.gethostbyname_ex() does not work for IPv6, and the Python documentation recommends one use socket.getaddressinfo() instead.

This routine adds the callback IPv4 interface (127.0.0.1), and if there are any IPv6 interfaces then it also adds the callback IPv6 interface (::1). On my machine, socket.getaddrinfo() will give me one or both of these but only if I have no other interfaces available.

For my needs, I wanted to try to open a UDP socket on a specified port on each of my available interfaces, which is why the code has "port" and socket.SOCK_DGRAM in it. It is safe to change those, e.g. if you don't have a port in mind.

addrinfo_ipv4 = socket.getaddrinfo(hostname,port,socket.AF_INET,socket.SOCK_DGRAM)
addrinfo_ipv6 = []
try:
    addrinfo_ipv6 = socket.getaddrinfo(hostname,port,socket.AF_INET6,socket.SOCK_DGRAM)
except socket.gaierror:
    pass
addrinfo = [(f,t,a) for f,t,p,cn,a in addrinfo_ipv4+addrinfo_ipv6]
addrinfo_local = [(socket.AF_INET,socket.SOCK_DGRAM,('127.0.0.1',port))]
if addrinfo_ipv6: 
    addrinfo_local.append( (socket.AF_INET6,socket.SOCK_DGRAM,('::1',port)) )
[addrinfo.append(ai) for ai in addrinfo_local if ai not in addrinfo]
DamonJW
  • 3,342
  • 2
  • 24
  • 29
0

You should directly obtain all IP configured IP addresses, e.g. by running ifconfig and parsing its output (it's also possible to do what ifconfig does directly in Python, see how it is done in C). If you want host names, use gethostbyaddr.

Community
  • 1
  • 1
Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
0

I think @Harley Holcombe's answer is workable, but if you have some virtual NICs without ip it will error. so this is i modified:

def get_lan_ip():
for interface in interfaces():
    try:
        for link in ifaddresses(interface)[AF_INET]:
            if str(link['addr']).startswith("172."):
                return str(link['addr'])
    except:
        pass

this will only return your lan ipv4

yongdi
  • 3
  • 3
0

thanks to @The Demz's answer. for my config, ubuntu 14.04(kernel 4.0.0), python3.4, it needs try-catch to skip some nic because it will got OSError on nic 'sit0' here's the modification:

def nic_info():
    """
    Return a list with tuples containing NIC and IPv4
    """
    nic = []
    for ix in socket.if_nameindex():
        name = ix[1]
        try:
            ip = get_ip_address( name )
            nic.append( (name, ip) )
        except OSError as e:
            pass
        except Exception as e:
            raise e
    return nic

Mic Ho
  • 41
  • 3
0

Here is example of get all ipv4 and ipv6 address.

import socket
from socket import AddressFamily


def get_ips():
    ipv4 = list()
    ipv6 = list()
    for item in socket.getaddrinfo(socket.gethostname(), None):
        protocol, *_, (ip, *_) = item
        if protocol == AddressFamily.AF_INET:
            ipv4.append(ip)
        elif protocol == AddressFamily.AF_INET6:
            ipv6.append(ip)

    return ipv4, ipv6


if __name__ == '__main__':
    all_ipv4, all_ipv6 = get_ips()
    print(all_ipv4)
    print(all_ipv6)

itchenfei
  • 19
  • 1
0

In python3:

from socket import getaddrinfo, AF_INET, gethostname
def ip4_addresses_list():
    ip_list = []
    for ip in getaddrinfo(host=gethostname(), port=None, family=AF_INET):   
        ip_list.append(ip[4][0])
    return ip_list
print(ip4_addresses_list())

result is like this:

['192.168.1.101', '192.168.56.10']
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 17 '23 at 12:41