views:

1119

answers:

5

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?

+1  A: 

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.

Martin v. Löwis
A: 

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
+5  A: 

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.

Harley
PS. This answer is fully tested, unlike the other highly upvoted one I gave. That (very wrong) answer has been deleted.
Harley
A: 

It turns out that what I wanted could be done by using the function socket.gethostbyname_ex() - which I hadn't realized existed - in the sockets module.

IMO, it's not a very good function name, but it does return all of the available IP addresses (as a list within a tuple of other stuff).

Thanks for all the other solutions, too!

Wilson Fowlie
just note that this isn't guaranteed to work the same on all systems, as it can be dependent on name resolution (note my answer above). My systems won't return external ip addresses for the fqdn, and won't return addresses bound to virtual adapters at all.
JimB
Another note that socket.gethostbyname_ex() will not return any addresses that you get via DHCP, and doesn't work for IPv6.
Harley
A: 

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