Finding local IP addresses using Python's stdlib
Asked Answered
M

50

732

How can I find local IP addresses (i.e. 192.168.x.x or 10.0.x.x) in Python platform independently and using only the standard library?

Member answered 3/10, 2008 at 12:3 Comment(6)
The local IP? Or public IP? How are you going to deal with systems with multiple IPs?Damage
use ifconfig -a and use the output from there...Westleigh
@Fredrik That's a bad idea. First of all, you're unnecessarily forking a new process, and that may prevent your program from working in tightly locked configurations (or, you'll have to allow rights your program doesn't need). Secondly, you'll introduce bugs for users of different locales. Thirdly, if you decide to start a new program at all, you shouldn't start a deprecated one - ip addr is far more suitable (and easier to parse, to boot).Norland
@Norland you are absolutely correct, thanks for correcting my stupidityWestleigh
A more fundamental problem here is that in a properly written modern networking program the right (set of) local IP address(es) depends on the peer, or the set of potential peers. If the local IP address is needed to bind a socket to a particular interface, then it is a policy matter. If the local IP address is needed to hand it over to a peer so that the peer can "call back", i.e. to open a connection back to the local machine, then the situation depends on whether there are any NAT (Network Address Translation) boxes in between. If there are no NATs, getsocknameis a good choice.Auditorium
What if there's NAT and I want to still use callback to connect to a socket.Venuti
A
569
import socket
socket.gethostbyname(socket.gethostname())

This won't work always (returns 127.0.0.1 on machines having the hostname in /etc/hosts as 127.0.0.1), a paliative would be what gimel shows, use socket.getfqdn() instead. Of course your machine needs a resolvable hostname.

Alecalecia answered 3/10, 2008 at 12:6 Comment(22)
One should note that this isn't a platform independent solution. A lot of Linuxes will return 127.0.0.1 as your IP address using this method.Laban
A variation: socket.gethostbyname(socket.getfqdn())Sirenasirenic
This appears to only return a single IP address. What if the machine has multiple addresses?Merry
On Ubuntu this returns 127.0.1.1 for some reason.Newborn
@Reinis I: on Ubuntu this returns 127.0.1.1 because of a line in /etc/hosts. This line can be removed without terrible consequences.Pokorny
Don't depend on DNS (and possibly cause blocking on network I/O) if all you want to know is a local hostname.Aluin
@Pokorny I have found that rabbitmq is unhappy if this line is missing from /etc/hostsBaronage
@amarillion: On some Ubuntu systems [which?] the user may loose the ability to run 'sudo' commands if 127.0.1.1 isn't in the hosts file. I've had this happen, and it is documented.Grath
@Member please do not accept the answer, it's clearly unreliable.Luminesce
@Jason R. Coombs, use following code to retrieve list of IPv4 addresses that belong to the host machine: socket.gethostbyname_ex(socket.gethostname())[-1]Caseose
This one works if you know the interface: https://mcmap.net/q/48466/-how-can-i-get-the-ip-address-from-a-nic-network-interface-controller-in-pythonPalisade
Can be a problem on Windows as well because I have VirtualBox, which installs its own network adapter. The command socket.gethostbyname(socket.gethostname()) returned the Virtualbox address. @JasonR.Coombs answer was helpful because it returned both. VirtualBox is first on mine but I don't know how it decides on the orderLeaflet
Owch. I have no idea why this answer got accepted. Please, people, just use the "connect to 8.8.8.8 and obtain that socket's local address" trick. At least it's guaranteed to work.Jataka
it works only if theres is single network interface, if you have multiple like ethernet and wireless, it shows wrong ip when you connect to wireless. use other answers that use getsockname() method to get correct ipTonnie
On Ubuntu 17.10, even @gimel's answer returns "127.0.0.1"Jeth
Every use of socket.getfqdn() introduces a delay of 8-10 seconds on my platform. (I'm using Windows on a large corporate network.)Aucoin
return '127.0.0.1'Borzoi
This does not return VPN addresses for exampleAnthropomorphous
I have an option here that can filter out IPv6 and loopback/link-local addresses, as well as validate that the output is a valid IP. #24197432Courtesy
I'm on an AWS EC2 and had to use a modification of @Caseose answer: socket.gethostbyname_ex(socket.gethostname())[-1][-1]Papyraceous
Both versions (including gimel's) returns 127.0.1.1 on a VM set up with both NAT and host-only adapter. socket.gethostbyname_ex() does not list any other interfaces other than 127.0.1.1. Connecting to 8.8.8.8 then getting the IP address works, but does not really seem straightforward.Buckeen
This is definitely the most straightforward answer.Whitten
M
644

I just found this but it seems a bit hackish, however they say tried it on *nix and I did on windows and it worked.

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])
s.close()

This assumes you have an internet access, and that there is no local proxy.

Member answered 3/10, 2008 at 12:35 Comment(24)
Nice if you have several interfaces on the machine, and needs the one which routes to e.g. gmail.comEvictee
It might be a good idea to catch socket.error exceptions which may be risen by s.connect()!Stereochrome
Nice, I want the IP some client saw when connecting to my service anyway.Impart
It would be better to use IP address instead of a domain name -- it must be faster and independent from DNS availability. E.g. we can use 8.8.8.8 IP -- Google's public DNS server.Sophia
Very clever, works perfectly. Instead of gmail or 8.8.8.8, you can also use the IP or address of the server you want to be seen from, if that is applicable.Leontineleontyne
@Prof.Falken Yep! It works perfectly from within a LAN environment too. You can connect to pretty much any machine listening on the port specified. I needed to use the server's IP address as a key for a Redis set so I just used the Redis server and port to get the IP and it worked like a charm! :-)Heddy
any way to get this to work with broadcast addresses instead of gmail.com?Glede
I like this. I'd like it better if I could figure out how to use the IP address of a vpn tunnel.Mucker
man, using connect on a UDP socket does nothing. But, it gives the socket the public ip. I used s.connect(("123.123.123.123",80))Westlund
This example has an external dependency of being able to actually resolve gmail.com. If you set it to an ip address that is not on your local lan (doesn't matter if it's up or down) it'll work without dependencies and without network traffic.Blue
As noted by @Blue it would be better to open a socket with a most likely available machine, since a lot of networked devices doesn't have free access to internet. I think your gateway or dhcp server might be the best. You can get your gateway in Linux with https://mcmap.net/q/64450/-python-get-default-gateway-for-a-local-interface-ip-address-in-linuxYod
@feisky I just confirmed this also works on OSX (running El Capitan)Spline
Clever, in this way one can find the right interface IP to a specify destination host, and without actually sending any packetAugmentative
To add to the 9 years worth of comments: This solution just worked for me on a standalone processor running Yocto Linux in 2017.Connett
This solution works, but I prefer not to use Google's IP address (8.8.8.8) but as Jamieson Becker pointed out, for example s.connect(('10.255.255.255', 1)).Rheingold
SOCK_DGRAM (UDP) sockets do not support connect. So you should stick to some TCP addressJanniejanos
As I suggested in an answer over 3 year before this one was last updated, you can replace the specific portname with s.connect(('<broadcast>',0)) but first you need to call s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)Brady
@Janniejanos "SOCK_DGRAM (UDP) sockets do not support connect. So you should stick to some TCP address". Not trueBrady
Using '<broadcast>' has the advantages of (a) not needing to be connected to the internet and (b) not needing to know any port numbers on the local network.Brady
This is the most clever solution I've seen, and seems battle tested from comments over the years. To further polish it a bit more, I suggest that port 53 with udp can be used for a nameserver like 8.8.8.8, and port 9 (the rfc 863 discard protocol) with udp/tcp for arbitrary hosts of other kinds.Wongawonga
In my book hackishness doesn't mean something is bad if it works well and can still be understood.Neils
This actually doesn't require internet access at all, just a network connection. It will just attempt to find 8.8.8.8. Can someone confirm this? if so an edit of the answer might be in orderAnthropomorphous
Super useful especially when net-tools are installed but for some mysterious reason can't run ifconfig. You can find out your local IP with only few lines of code. Python simply rocks.Rhombencephalon
Amazing! This is the best solution and solves my multi-ip addresses issue on both Linux and WindowsBlest
A
569
import socket
socket.gethostbyname(socket.gethostname())

This won't work always (returns 127.0.0.1 on machines having the hostname in /etc/hosts as 127.0.0.1), a paliative would be what gimel shows, use socket.getfqdn() instead. Of course your machine needs a resolvable hostname.

Alecalecia answered 3/10, 2008 at 12:6 Comment(22)
One should note that this isn't a platform independent solution. A lot of Linuxes will return 127.0.0.1 as your IP address using this method.Laban
A variation: socket.gethostbyname(socket.getfqdn())Sirenasirenic
This appears to only return a single IP address. What if the machine has multiple addresses?Merry
On Ubuntu this returns 127.0.1.1 for some reason.Newborn
@Reinis I: on Ubuntu this returns 127.0.1.1 because of a line in /etc/hosts. This line can be removed without terrible consequences.Pokorny
Don't depend on DNS (and possibly cause blocking on network I/O) if all you want to know is a local hostname.Aluin
@Pokorny I have found that rabbitmq is unhappy if this line is missing from /etc/hostsBaronage
@amarillion: On some Ubuntu systems [which?] the user may loose the ability to run 'sudo' commands if 127.0.1.1 isn't in the hosts file. I've had this happen, and it is documented.Grath
@Member please do not accept the answer, it's clearly unreliable.Luminesce
@Jason R. Coombs, use following code to retrieve list of IPv4 addresses that belong to the host machine: socket.gethostbyname_ex(socket.gethostname())[-1]Caseose
This one works if you know the interface: https://mcmap.net/q/48466/-how-can-i-get-the-ip-address-from-a-nic-network-interface-controller-in-pythonPalisade
Can be a problem on Windows as well because I have VirtualBox, which installs its own network adapter. The command socket.gethostbyname(socket.gethostname()) returned the Virtualbox address. @JasonR.Coombs answer was helpful because it returned both. VirtualBox is first on mine but I don't know how it decides on the orderLeaflet
Owch. I have no idea why this answer got accepted. Please, people, just use the "connect to 8.8.8.8 and obtain that socket's local address" trick. At least it's guaranteed to work.Jataka
it works only if theres is single network interface, if you have multiple like ethernet and wireless, it shows wrong ip when you connect to wireless. use other answers that use getsockname() method to get correct ipTonnie
On Ubuntu 17.10, even @gimel's answer returns "127.0.0.1"Jeth
Every use of socket.getfqdn() introduces a delay of 8-10 seconds on my platform. (I'm using Windows on a large corporate network.)Aucoin
return '127.0.0.1'Borzoi
This does not return VPN addresses for exampleAnthropomorphous
I have an option here that can filter out IPv6 and loopback/link-local addresses, as well as validate that the output is a valid IP. #24197432Courtesy
I'm on an AWS EC2 and had to use a modification of @Caseose answer: socket.gethostbyname_ex(socket.gethostname())[-1][-1]Papyraceous
Both versions (including gimel's) returns 127.0.1.1 on a VM set up with both NAT and host-only adapter. socket.gethostbyname_ex() does not list any other interfaces other than 127.0.1.1. Connecting to 8.8.8.8 then getting the IP address works, but does not really seem straightforward.Buckeen
This is definitely the most straightforward answer.Whitten
R
477

This method returns the "primary" IP on the local box (the one with a default route).

  • Does NOT need routable net access or any connection at all.
  • Works even if all interfaces are unplugged from the network.
  • Does NOT need or even try to get anywhere else.
  • Works with NAT, public, private, external, and internal IP's
  • Pure Python 2 (or 3) with no external dependencies.
  • Works on Linux, Windows, and OSX.

Python 3 or 2:

    import socket
    def get_ip():
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.settimeout(0)
        try:
            # doesn't even have to be reachable
            s.connect(('10.254.254.254', 1))
            IP = s.getsockname()[0]
        except Exception:
            IP = '127.0.0.1'
        finally:
            s.close()
        return IP
    print(get_ip())

This returns a single IP which is the primary (the one with a default route). If you need instead all IP's attached to all interfaces (including localhost, etc), see something like this answer.

If you are behind a NAT firewall like your wifi router at home, then this will not show your public NAT IP, but instead your private IP on the local network which has a default route to your local WIFI router. If you instead need your external IP:

  • running this function on THAT external device (wifi router), or

  • connecting to an external service such as https://www.ipify.org/ that could reflect back the IP as it's seen from the outside world

... but those ideas are completely different from the original question. :)

Ric answered 9/3, 2015 at 20:2 Comment(21)
Works in Raspbian with Python 2 and 3 !Barrelhouse
Brilliant. Works on Win7,8,8.1 + Linux Mint & Arch, including VMs.Conduction
Works in Windows 10 Pro! Thank you, Jamieson Becker!Convertite
For some reason this does not work on Mac OS X El Capitan 10.11.6 (it generates an exception OS error: [Errno 49] Can't assign requested address). Changing the port from '0' to '1' : s.connect(('10.255.255.255', 1)) worked for me both on Mac OS X and Linux Ubuntu 17.04Cowbell
This should be the accepted answer. socket.gethostbyname(socket.gethostname()) gives horrible results.Bullion
Mac OSX 10.14.3 (18D109) returns 127.0.0.1 as primary IP addr when no route to internet exists yet the router is assigning a LAN IP via DHCP. Still could be a 192.168.1.x assigned which would be a valid and likely preferred LAN address.Integument
@RichAndrews That situation is discussed in the answer: "This returns a single IP which is the primary (the one with a default route)." 127.0.0.1 would be the expected response if you have no route to the internet.Ric
Would this work with any private network IP? E.g. 10.0.0.0 also works for me.Reconstitute
Works on AWS EC2 centos-based AMI. On MacOS-10.14 connected to corporate VPN returns IP from utun1 interface (assume that's VPN ip)Papyraceous
agree, this answer is the only one that truly works. I also added this solution implemented in nodejsAndresandresen
@fatal_error, can you tell some more info about this method of get private ip? I not understand how if i use border of private subnet 10. - how i get my private idress if my subnet is 192? how its magic works? (sorry for my bad english. I am just learning to tell withaout googl e translate)Einkorn
@Einkorn No change. Just use it like that. This will return the main, routable IP through which you would normally route packets to your default gateway. So, if your default gateway is 192.168.1.1 and your box IP address is 192.168.1.99, this will return your box IP address of 192.168.1.99, even if you have other interfaces configured. The 10.255.255.255 was chosen because you cannot actually use it as your IP address (it's a broadcast address).Ric
@djvg, yes, in general you can use it with any IP address that would be accessed through your default router.Ric
@ChrisWolf probably because that's where your default gateway is pointing at (into the tunnel)Ric
@Ric I also have an app called "IP Broadcaster" which correctly displays my broadband address even while connected to corporate VPN. apps.apple.com/us/app/ip-broadcaster/id520207162?mt=12 It would be interesting to see what they're doing.Papyraceous
Ok, I ran "strings" on "IP Broadcaster" and it's actually calling out to a web service: checkip.dyndns.com (that's cheating, sort-of)Papyraceous
@ChrisWolf right on :) also see last paragraph in the answer.Ric
This answer works for me! I also added s.settimeout(0) as my laptop(Win10) actually tries to connect and it takes a few seconds to return the result.Deina
@Deina excellent suggestion, added to answer!Ric
I get a Permission denied exception with this, on Ubuntu 22.04Eau
@Bram, Good catch, can't connect to broadcast address anymore :( Perhaps try it now with 10.254.254.254 (doesn't have to be a real IP). Thanks!Ric
S
166

As an alias called myip:

alias myip="python -c 'import socket; print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith(\"127.\")][:1], [[(s.connect((\"8.8.8.8\", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])'"
  • Works correctly with Python 2.x, Python 3.x, modern and old Linux distros, OSX/macOS and Windows for finding the current IPv4 address.
  • Will not return the correct result for machines with multiple IP addresses, IPv6, no configured IP address or no internet access.
  • Reportedly, this does not work on the latest releases of macOS.

NOTE: If you intend to use something like this within a Python program, the proper way is to make use of a Python module that has IPv6 support.


Same as above, but only the Python code:

import socket
print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])
  • This will throw an exception if no IP address is configured.

Version that will also work on LANs without an internet connection:

import socket
print((([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0])

(thanks @ccpizza)


Background:

Using socket.gethostbyname(socket.gethostname()) did not work here, because one of the computers I was on had an /etc/hosts with duplicate entries and references to itself. socket.gethostbyname() only returns the last entry in /etc/hosts.

This was my initial attempt, which weeds out all addresses starting with "127.":

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

This works with Python 2 and 3, on Linux and Windows, but does not deal with several network devices or IPv6. However, it stopped working on recent Linux distros, so I tried this alternative technique instead. It tries to connect to the Google DNS server at 8.8.8.8 at port 53:

import socket
print([(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])

This method also works on the latest versions of macOS!

Then I combined the two above techniques into a one-liner that should work everywhere, and created the myip alias and Python snippet at the top of this answer.

With the increasing popularity of IPv6, and for servers with multiple network interfaces, using a third-party Python module for finding the IP address is probably both more robust and reliable than any of the methods listed here.

Skeen answered 12/8, 2009 at 17:20 Comment(18)
socket.gethostbyname_ex('') appears to work great without relying on the hostname of the computer. It happens to also return said hostname as the first item in tuple. So, socket.gethostbyname_ex('')[2] gives me all my local IPs and /not/ 127.0.0.1 for some reason. This is on Python 2.6.6 32bit, Windows 7 64bit.Rhombencephalon
Forgot to note, that the code in my comment above (cannot edit any longer) also gives me VPN IP addresses, which was important to me when I came here looking for an answer.Rhombencephalon
Odd I am not getting VPN IP's. (Thats what I am trying to access also) The above gives me my standard internal IP but not the IP for the VPN I am also connected tooMausoleum
kdbdallas, I don't know if you're on win/osx or Linux, but I think VPN connections usually appear as a separate network device, in this context.Skeen
Unfortunately, this won't return IPv6 addresses - it seems that you need socket.getaddrinfo() for that. In other words, the answer by Nakilon way below will give you better results.Understood
@Wladimir Palant, I already mentioned that. Besides, his implementation may return duplicate IP adresses. He also doesn't mention if it works on Windows, OSX and Linux or not.Skeen
@Alexander: Just saying that this answer is much less useful than it used to be (and it's not like filtering out duplicates is a big deal ;). According to documentation socket.getaddrinfo() should work consistently across platforms - but I only checked it on Linux, didn't bother about any other operating systems.Understood
@Alexander, /etc/resolve.conf: No such file or directory and I have local IPv4 address shown by ifconfig.Bloodandthunder
I can confirm that the updated version works with Ubuntu 14.04 with both Python2 and Py3k.Contuse
The "update" shows a nice trick with connect() on a UDP socket. It sends no traffic but does let you find what would be the sender address for packets to the specified recipient. The port is likely irrelevant (even 0 should work). On a multihomed host it's important to pick an address in the right subnet.Icing
It seems I can not connect to 8.8.8.8 on port 80 - anyone else has this problem? google.com on the other hand works finePtosis
Chris, does it work if you try with port 53 instead?Skeen
I found this answer useful and ended up using socket.gethostbyname_ex(socket.gethostname())[2] which works for my needs which is dual-homed Arch Linux and python2/3. I am not sure what I am missing by not using the whole of the snippet given in the answer.Bi
I guess you'll only be missing an alias in your shell config (that you share among the systems you have access to, regardless of OS, I presume), that will work on all of them, under normal circumstances.Skeen
The combined alias code initiates an unnecessary external connection to 8.8.8.8 even if gethostbyname_ex returned a valid IP. This will break in 'walled garden'-type of LANs with no internet. The external call can be made conditional by using or, e.g.: ips = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]Electrobiology
@JackLeo It works as an alias on the command line. If you want to do it properly, you should use a Python module that correctly handles IPv6 and multiple network interfaces in the first place.Skeen
The proper way is to use a Python module with IPv6 support, not any of the answers on this page.Skeen
I think the one-liner is cool - of course you can clean it up for real use. It works on AWS EC2 running CentOS-based AMI, thanks.Papyraceous
C
105

You can use the netifaces module. Just type:

pip install netifaces

in your command shell and it will install itself on default Python installation.

Then you can use it like this:

from netifaces import interfaces, ifaddresses, AF_INET
for ifaceName in interfaces():
    addresses = [i['addr'] for i in ifaddresses(ifaceName).setdefault(AF_INET, [{'addr':'No IP addr'}] )]
    print '%s: %s' % (ifaceName, ', '.join(addresses))

On my computer it printed:

{45639BDC-1050-46E0-9BE9-075C30DE1FBC}: 192.168.0.100
{D43A468B-F3AE-4BF9-9391-4863A4500583}: 10.5.9.207

Author of this module claims it should work on Windows, UNIX and Mac OS X.

Chappy answered 3/10, 2008 at 12:35 Comment(6)
As stated in the question I want something from the default install, as in no additional installs needed.Member
@MattJoiner Neither of this things is true any more (the latest version has Windows binaries on PyPI and does support Py3K).Promissory
@Jean-PaulCalderone FWIW, the latest version of netifaces does support IPv6 on Windows.Promissory
this module must be part of the standard library, given that python claims a 'batteries included' philosophyElectrobiology
raise a bug, maybe it'll get included into sys it seems like a good candidate for that.Pickerelweed
@MattJoiner - Note that on Ubuntu, the latest version requires no C compiler for either python or Py3K. And there are packages for the module as well.Sedimentology
H
56

If the computer has a route to the Internet, this will always work to get the preferred local ip address, even if /etc/hosts is not set correctly.

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1))  # connect() for UDP doesn't send packets
local_ip_address = s.getsockname()[0]
Hesperian answered 15/9, 2014 at 14:43 Comment(2)
how does this work ? , 8.8.8.8 is a google dns server can we do it with a local dns server ?Plaster
@Ciastopiekarz The address does not have to be valid. See: https://mcmap.net/q/63357/-finding-local-ip-addresses-using-python-39-s-stdlibBuckeen
I
52

Socket API method

see https://mcmap.net/q/63357/-finding-local-ip-addresses-using-python-39-s-stdlib

Downsides:

  • Not cross-platform.
  • Requires more fallback code, tied to existence of particular addresses on the internet
  • This will also not work if you're behind a NAT
  • Probably creates a UDP connection, not independent of (usually ISP's) DNS availability (see other answers for ideas like using 8.8.8.8: Google's (coincidentally also DNS) server)
  • Make sure you make the destination address UNREACHABLE, like a numeric IP address that is spec-guaranteed to be unused. Do NOT use some domain like fakesubdomain.google.com or somefakewebsite.com; you'll still be spamming that party (now or in the future), and spamming your own network boxes as well in the process.

Reflector method

(Do note that this does not answer the OP's question of the local IP address, e.g. 192.168...; it gives you your public IP address, which might be more desirable depending on use case.)

You can query some site like whatismyip.com (but with an API), such as:

from urllib.request import urlopen
import re
def getPublicIp():
    data = str(urlopen('http://checkip.dyndns.com/').read())
    # data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'

    return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)

or if using python2:

from urllib import urlopen
import re
def getPublicIp():
    data = str(urlopen('http://checkip.dyndns.com/').read())
    # data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'

    return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)

Advantages:

  • One upside of this method is it's cross-platform
  • It works from behind ugly NATs (e.g. your home router).

Disadvantages (and workarounds):

  • Requires this website to be up, the format to not change (almost certainly won't), and your DNS servers to be working. One can mitigate this issue by also querying other third-party IP address reflectors in case of failure.
  • Possible attack vector if you don't query multiple reflectors (to prevent a compromised reflector from telling you that your address is something it's not), or if you don't use HTTPS (to prevent a man-in-the-middle attack pretending to be the server)

edit: Though initially I thought these methods were really bad (unless you use many fallbacks, the code may be irrelevant many years from now), it does pose the question "what is the internet?". A computer may have many interfaces pointing to many different networks. For a more thorough description of the topic, google for gateways and routes. A computer may be able to access an internal network via an internal gateway, or access the world-wide web via a gateway on for example a router (usually the case). The local IP address that the OP asks about is only well-defined with respect to a single link layer, so you have to specify that ("is it the network card, or the ethernet cable, which we're talking about?"). There may be multiple non-unique answers to this question as posed. However the global IP address on the world-wide web is probably well-defined (in the absence of massive network fragmentation): probably the return path via the gateway which can access the TLDs.

Indefectible answered 23/6, 2011 at 11:7 Comment(3)
This will return your LAN-wide address if you're behind a NAT. If you're connecting to the Internet, you can connect to a web service that returns one of your public IP addresses.Norland
It doesn't create a TCP connection because it creates a UDP connection.Pagano
As an alternative in the socket API version, replace s.connect(('INSERT SOME TARGET WEBSITE.com', 0)) with s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1);s.connect(('<broadcast>', 0)) to avoid DNS lookup. (I guess there might be a problem with a broadcast if there is a firewall)Brady
M
41

On Linux:

>>> import socket, struct, fcntl
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> sockfd = sock.fileno()
>>> SIOCGIFADDR = 0x8915
>>>
>>> def get_ip(iface = 'eth0'):
...     ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
...     try:
...         res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
...     except:
...         return None
...     ip = struct.unpack('16sH2x4s8x', res)[2]
...     return socket.inet_ntoa(ip)
... 
>>> get_ip('eth0')
'10.80.40.234'
>>> 
Merideth answered 13/2, 2012 at 20:52 Comment(7)
So this effectively opens a socket that it does nothing with and you check the raw data about that socket to get the local IP?Basenji
The socket is opened to get an fd to communicate with the kernel (via ioctl). The socket isn't bound the interface for which you want addr info about- its just a communication mechanism between userspace and the kernel. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.cMerideth
Works on Python3 with one modification: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14) should be replaced with struct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)Materials
how can i do the same for ipv6?Urdar
@ChristianFischer ioctl is a legacy interface I don't believe supports IPv6 and likely never will. I think the 'Right' way is via Netlink which isn't very straightforward in Python. I think libc should have the function getifaddrs which can be accessed via pythons ctypes module which may work - man7.org/linux/man-pages/man3/getifaddrs.3.htmlMerideth
@Maddy ioctl is a legacy interface I don't believe supports IPv6 and likely never will. I think the 'Right' way is via Netlink which isn't very straightforward in Python. I think libc should have the function getifaddrs which can be accessed via pythons ctypes module which may work - man7.org/linux/man-pages/man3/getifaddrs.3.htmlMerideth
This is nice because it doesn't require an internet connection.Percutaneous
D
28

im using following module:

#!/usr/bin/python
# module for getting the lan ip address of the computer

import os
import socket

if os.name != "nt":
    import fcntl
    import struct
    def get_interface_ip(ifname):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(
                s.fileno(),
                0x8915,  # SIOCGIFADDR
                struct.pack('256s', bytes(ifname[:15], 'utf-8'))
                # Python 2.7: remove the second argument for the bytes call
            )[20:24])

def get_lan_ip():
    ip = socket.gethostbyname(socket.gethostname())
    if ip.startswith("127.") and os.name != "nt":
        interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
        for ifname in interfaces:
            try:
                ip = get_interface_ip(ifname)
                break;
            except IOError:
                pass
    return ip

Tested with windows and linux (and doesnt require additional modules for those) intended for use on systems which are in a single IPv4 based LAN.

The fixed list of interface names does not work for recent linux versions, which have adopted the systemd v197 change regarding predictable interface names as pointed out by Alexander. In such cases, you need to manually replace the list with the interface names on your system, or use another solution like netifaces.

Decolorant answered 22/12, 2009 at 17:7 Comment(3)
This is incompatible with the new predictable Linux interface names, such as enp0s25. For more info, see wiki.archlinux.org/index.php/Network_Configuration#Device_namesSkeen
I was using python 3.4 and the 'struct.pack(...)' part needed to be changed to 'struct.pack('256s', bytes(ifname[:15], 'utf-8'))'. See this question: https://mcmap.net/q/64451/-struct-error-argument-for-39-s-39-must-be-a-bytes-object-in-python-3-4/76010Stairway
on Raspbian w/ Python 2.7.3 - bytes() did not like the 2nd argument. But this worked: struct.pack('256s', bytes(ifname[:15]))Mitsukomitt
C
24

[Windows only] If you don't want to use external packages and don't want to rely on outside Internet servers, this might help. It's a code sample that I found on Google Code Search and modified to return required information:

def getIPAddresses():
    from ctypes import Structure, windll, sizeof
    from ctypes import POINTER, byref
    from ctypes import c_ulong, c_uint, c_ubyte, c_char
    MAX_ADAPTER_DESCRIPTION_LENGTH = 128
    MAX_ADAPTER_NAME_LENGTH = 256
    MAX_ADAPTER_ADDRESS_LENGTH = 8
    class IP_ADDR_STRING(Structure):
        pass
    LP_IP_ADDR_STRING = POINTER(IP_ADDR_STRING)
    IP_ADDR_STRING._fields_ = [
        ("next", LP_IP_ADDR_STRING),
        ("ipAddress", c_char * 16),
        ("ipMask", c_char * 16),
        ("context", c_ulong)]
    class IP_ADAPTER_INFO (Structure):
        pass
    LP_IP_ADAPTER_INFO = POINTER(IP_ADAPTER_INFO)
    IP_ADAPTER_INFO._fields_ = [
        ("next", LP_IP_ADAPTER_INFO),
        ("comboIndex", c_ulong),
        ("adapterName", c_char * (MAX_ADAPTER_NAME_LENGTH + 4)),
        ("description", c_char * (MAX_ADAPTER_DESCRIPTION_LENGTH + 4)),
        ("addressLength", c_uint),
        ("address", c_ubyte * MAX_ADAPTER_ADDRESS_LENGTH),
        ("index", c_ulong),
        ("type", c_uint),
        ("dhcpEnabled", c_uint),
        ("currentIpAddress", LP_IP_ADDR_STRING),
        ("ipAddressList", IP_ADDR_STRING),
        ("gatewayList", IP_ADDR_STRING),
        ("dhcpServer", IP_ADDR_STRING),
        ("haveWins", c_uint),
        ("primaryWinsServer", IP_ADDR_STRING),
        ("secondaryWinsServer", IP_ADDR_STRING),
        ("leaseObtained", c_ulong),
        ("leaseExpires", c_ulong)]
    GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo
    GetAdaptersInfo.restype = c_ulong
    GetAdaptersInfo.argtypes = [LP_IP_ADAPTER_INFO, POINTER(c_ulong)]
    adapterList = (IP_ADAPTER_INFO * 10)()
    buflen = c_ulong(sizeof(adapterList))
    rc = GetAdaptersInfo(byref(adapterList[0]), byref(buflen))
    if rc == 0:
        for a in adapterList:
            adNode = a.ipAddressList
            while True:
                ipAddr = adNode.ipAddress
                if ipAddr:
                    yield ipAddr
                adNode = adNode.next
                if not adNode:
                    break

Usage:

>>> for addr in getIPAddresses():
>>>    print addr
192.168.0.100
10.5.9.207

As it relies on windll, this will work only on Windows.

Chappy answered 3/10, 2008 at 13:54 Comment(4)
The one liner solution above generally works on windows. It's the Linux one that's being a problem.Kissner
+1 This technique at least attempts to return all addresses on the machine.Merry
This script fails on my machine after returning the first address. Error is "AttributeError: 'LP_IP_ADDR_STRING' object has no attribute 'ipAddress'" I suspect it has something to do with the IPv6 address.Merry
It turns out the issue is that for anything but the first IP address, the adNode isn't dereferenced. Add one more line to the example in the while loop and it works for me: adNode = adNode.contentsMerry
B
24

Variation on ninjagecko's answer. This should work on any LAN that allows UDP broadcast and doesn't require access to an address on the LAN or internet.

import socket
def getNetworkIp():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.connect(('<broadcast>', 0))
    return s.getsockname()[0]

print (getNetworkIp())
Brady answered 23/5, 2014 at 6:30 Comment(8)
Wait, how is <broadcast> a valid hostname?!! How many of these kinds of verbal hostnames are valid?Rimmer
This works for me on Ubuntu 20.04 - getting 192.168.0.24 not 127.0.0.1Telethermometer
Works on every system I've tested it on. Windows, Android, Linux, Mac.Brady
When I use <broadcast> on Windows 10, I get Wireless NIC IP address. When I use 10.255.255.255 then I get Ethernet TAP Adapter's IP address. Both are valid. But results re different, lol.Backstroke
Works like a charm on Ubuntu 21.04, thanks !Gobi
Did not work on Mac OS 11.1. Saw this error: socket.error: [Errno 49] Can't assign requested addressRomito
Did not work on Mac OS 11.1 <-- Thank you, Apple! :PBrady
Does not work on Macbook Pro (ARM) running Sonama 14.4.1 <-- Thank you again, Apple!Pogue
B
23

I use this on my ubuntu machines:

import commands
commands.getoutput("/sbin/ifconfig").split("\n")[1].split()[1][5:]

This doesn't work.

Byroad answered 5/7, 2010 at 4:45 Comment(9)
Nice and simple. Works on Amazon's Linux AMI as well, but only if I am root. Otherwise I would get an error: 'sh: ifconfig: command not found'Extra
So you should use "/sbin/ifconfig" like gavaletz said. It also works on Red Hat 4.1.2-48.Extra
Deprecated since 2.6. Use the subprocess module to run commands.Aguie
And ifconfig is deprecated as well. Use iproute2.Incarcerate
Get all the ips: import sh; [ip.split()[1][5:] for ip in filter(lambda x: 'inet addr' in x, sh.ifconfig().split("\n"))]Hook
this gives me localhost 127.0.0.1 ip on raspbianHilbert
How do we know that the output format of ipconfig never changes?Ru
because it's deprecated !Pickerelweed
How did this gather so many upvotes? This was never platform independent.Salimeter
E
19

On Debian (tested) and I suspect most Linux's..

import commands

RetMyIP = commands.getoutput("hostname -I")

On MS Windows (tested)

import socket

socket.gethostbyname(socket.gethostname())
Either answered 3/7, 2014 at 23:19 Comment(2)
Doesn't work on macOS: hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]Mauriciomaurie
In python 3 you need to replace "commands" with "subprocess", the rest is the sameSusy
S
18

A version I do not believe that has been posted yet. I tested with python 2.7 on Ubuntu 12.04.

Found this solution at : http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/

import socket
import fcntl
import struct

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

Example Result:

>>> get_ip_address('eth0')
'38.113.228.130'
Scruggs answered 5/1, 2015 at 22:3 Comment(1)
Works on Python3, Ubuntu 18.04; The string needs to be bytes: >>> socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', 'enp0s31f6'[:15].encode('utf-8')))[20:24]) '192.168.1.1'Audette
P
11

For linux, you can just use check_output of the hostname -I system command like so:

from subprocess import check_output
check_output(['hostname', '-I'])
Pacer answered 25/7, 2019 at 7:36 Comment(2)
for googlers, I know the question was for a cross platform solutionPacer
To get the exact IP address check_output(['hostname', '-I']).decode().strip()Fanaticism
M
10

This is a variant of UnkwnTech's answer -- it provides a get_local_addr() function, which returns the primary LAN ip address of the host. I'm posting it because this adds a number of things: ipv6 support, error handling, ignoring localhost/linklocal addrs, and uses a TESTNET addr (rfc5737) to connect to.

# imports
import errno
import socket
import logging

# localhost prefixes
_local_networks = ("127.", "0:0:0:0:0:0:0:1")

# ignore these prefixes -- localhost, unspecified, and link-local
_ignored_networks = _local_networks + ("0.", "0:0:0:0:0:0:0:0", "169.254.", "fe80:")

def detect_family(addr):
    if "." in addr:
        assert ":" not in addr
        return socket.AF_INET
    elif ":" in addr:
        return socket.AF_INET6
    else:
        raise ValueError("invalid ipv4/6 address: %r" % addr)

def expand_addr(addr):
    """convert address into canonical expanded form --
    no leading zeroes in groups, and for ipv6: lowercase hex, no collapsed groups.
    """
    family = detect_family(addr)
    addr = socket.inet_ntop(family, socket.inet_pton(family, addr))
    if "::" in addr:
        count = 8-addr.count(":")
        addr = addr.replace("::", (":0" * count) + ":")
        if addr.startswith(":"):
            addr = "0" + addr
    return addr

def _get_local_addr(family, remote):
    try:
        s = socket.socket(family, socket.SOCK_DGRAM)
        try:
            s.connect((remote, 9))
            return s.getsockname()[0]
        finally:
            s.close()
    except socket.error:
        # log.info("trapped error connecting to %r via %r", remote, family, exc_info=True)
        return None

def get_local_addr(remote=None, ipv6=True):
    """get LAN address of host

    :param remote:
        return  LAN address that host would use to access that specific remote address.
        by default, returns address it would use to access the public internet.

    :param ipv6:
        by default, attempts to find an ipv6 address first.
        if set to False, only checks ipv4.

    :returns:
        primary LAN address for host, or ``None`` if couldn't be determined.
    """
    if remote:
        family = detect_family(remote)
        local = _get_local_addr(family, remote)
        if not local:
            return None
        if family == socket.AF_INET6:
            # expand zero groups so the startswith() test works.
            local = expand_addr(local)
        if local.startswith(_local_networks):
            # border case where remote addr belongs to host
            return local
    else:
        # NOTE: the two addresses used here are TESTNET addresses,
        #       which should never exist in the real world.
        if ipv6:
            local = _get_local_addr(socket.AF_INET6, "2001:db8::1234")
            # expand zero groups so the startswith() test works.
            if local:
                local = expand_addr(local)
        else:
            local = None
        if not local:
            local = _get_local_addr(socket.AF_INET, "192.0.2.123")
            if not local:
                return None
    if local.startswith(_ignored_networks):
        return None
    return local
Monopteros answered 20/12, 2013 at 18:41 Comment(2)
I thought this could have been a really good answer.. but it always return NoneOctal
@JamieLindsey Do you have some details about your OS, network configuration? Also, what does something like get_local_addr(remove="www.google.com") return? Logging the socket.error thrown by _get_local_addr() might help diagnostically.Monopteros
L
8

I'm afraid there aren't any good platform independent ways to do this other than connecting to another computer and having it send you your IP address. For example: findmyipaddress. Note that this won't work if you need an IP address that's behind NAT unless the computer you're connecting to is behind NAT as well.

Here's one solution that works in Linux: get the IP address associated with a network interface.

Laban answered 3/10, 2008 at 12:7 Comment(0)
R
8

FYI I can verify that the method:

import socket
addr = socket.gethostbyname(socket.gethostname())

Works in OS X (10.6,10.5), Windows XP, and on a well administered RHEL department server. It did not work on a very minimal CentOS VM that I just do some kernel hacking on. So for that instance you can just check for a 127.0.0.1 address and in that case do the following:

if addr == "127.0.0.1":
     import commands
     output = commands.getoutput("/sbin/ifconfig")
     addr = parseaddress(output)

And then parse the ip address from the output. It should be noted that ifconfig is not in a normal user's PATH by default and that is why I give the full path in the command. I hope this helps.

Ronnieronny answered 30/12, 2009 at 15:51 Comment(1)
This is what I see on Mac OS 11.1: socket.gaierror: [Errno 8] nodename nor servname provided, or not knownRomito
G
8

One simple way to produce "clean" output via command line utils:

import commands
ips = commands.getoutput("/sbin/ifconfig | grep -i \"inet\" | grep -iv \"inet6\" | " +
                         "awk {'print $2'} | sed -ne 's/addr\:/ /p'")
print ips

It will show all IPv4 addresses on the system.

Grit answered 13/6, 2011 at 7:21 Comment(2)
It will not show all IPv4 addresses, because ifconfig only tells you about primary ones. You need to use "ip" from iproute2 to see all addresses.Incarcerate
That's a hell of a lot of shell for a question asking for the standard library… Also, parsing ifconfig is neither portable and will not even work reliably on one machine.Trapper
C
6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Comprehend answered 7/5, 2013 at 6:54 Comment(2)
Hmm...on a server with two NICs this gives one of the assigned IP addresses, but repeated three times. On my laptop it gives '127.0.1.1' (repeated three times...)...Stalactite
Gives me ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2'] on Windows desktop.Comprehend
B
5

This will work on most linux boxes:

import socket, subprocess, re
def get_ipv4_address():
    """
    Returns IP address(es) of current machine.
    :return:
    """
    p = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE)
    ifc_resp = p.communicate()
    patt = re.compile(r'inet\s*\w*\S*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
    resp = patt.findall(ifc_resp[0])
    print resp

get_ipv4_address()
Broughton answered 27/4, 2012 at 12:14 Comment(0)
D
5

This answer is my personal attempt to solve the problem of getting the LAN IP, since socket.gethostbyname(socket.gethostname()) also returned 127.0.0.1. This method does not require Internet just a LAN connection. Code is for Python 3.x but could easily be converted for 2.x. Using UDP Broadcast:

import select
import socket
import threading
from queue import Queue, Empty

def get_local_ip():
        def udp_listening_server():
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.bind(('<broadcast>', 8888))
            s.setblocking(0)
            while True:
                result = select.select([s],[],[])
                msg, address = result[0][0].recvfrom(1024)
                msg = str(msg, 'UTF-8')
                if msg == 'What is my LAN IP address?':
                    break
            queue.put(address)

        queue = Queue()
        thread = threading.Thread(target=udp_listening_server)
        thread.queue = queue
        thread.start()
        s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        waiting = True
        while waiting:
            s2.sendto(bytes('What is my LAN IP address?', 'UTF-8'), ('<broadcast>', 8888))
            try:
                address = queue.get(False)
            except Empty:
                pass
            else:
                waiting = False
        return address[0]

if __name__ == '__main__':
    print(get_local_ip())
Divisor answered 12/6, 2012 at 8:15 Comment(1)
What happens if you run this simultaneously on two machines on the same network ? As you broadcast your message on the network, all the machines will receive the 'What is my LAN IP address. Your udp_listening_server could reply 'your IP address is xxx' to the message.Galaxy
S
5

If you're looking for an IPV4 address different from your localhost IP address 127.0.0.1, here is a neat piece of python codes:

import subprocess
address = subprocess.check_output(['hostname', '-s', '-I'])
address = address.decode('utf-8') 
address=address[:-1]

Which can also be written in a single line:

address = subprocess.check_output(['hostname', '-s', '-I']).decode('utf-8')[:-1]

Even if you put localhost in /etc/hostname, the code will still give your local IP address.

Snub answered 14/3, 2018 at 4:45 Comment(0)
N
4

127.0.1.1 is your real IP address. More generally speaking, a computer can have any number of IP addresses. You can filter them for private networks - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.

However, there is no cross-platform way to get all IP addresses. On Linux, you can use the SIOCGIFCONF ioctl.

Norland answered 23/6, 2011 at 11:4 Comment(1)
He means his externally visible IP. The 127.*.*.* range typically refers to localhost or an internal network, which is clearly not what he wants.Marcelinomarcell
H
4

A slight refinement of the commands version that uses the IP command, and returns IPv4 and IPv6 addresses:

import commands,re,socket

#A generator that returns stripped lines of output from "ip address show"
iplines=(line.strip() for line in commands.getoutput("ip address show").split('\n'))

#Turn that into a list of IPv4 and IPv6 address/mask strings
addresses1=reduce(lambda a,v:a+v,(re.findall(r"inet ([\d.]+/\d+)",line)+re.findall(r"inet6 ([\:\da-f]+/\d+)",line) for line in iplines))
#addresses1 now looks like ['127.0.0.1/8', '::1/128', '10.160.114.60/23', 'fe80::1031:3fff:fe00:6dce/64']

#Get a list of IPv4 addresses as (IPstring,subnetsize) tuples
ipv4s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if '.' in addr)]
#ipv4s now looks like [('127.0.0.1', 8), ('10.160.114.60', 23)]

#Get IPv6 addresses
ipv6s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if ':' in addr)]
Haily answered 30/4, 2012 at 0:32 Comment(0)
G
4

Well you can use the command "ip route" on GNU/Linux to know your current IP address.

This shows the IP given to the interface by the DHCP server running on the router/modem. Usually "192.168.1.1/24" is the IP for local network where "24" means the range of posible IP addresses given by the DHCP server within the mask range.

Here's an example: Note that PyNotify is just an addition to get my point straight and is not required at all

#! /usr/bin/env python

import sys , pynotify

if sys.version_info[1] != 7:
   raise RuntimeError('Python 2.7 And Above Only')       

from subprocess import check_output # Available on Python 2.7+ | N/A 

IP = check_output(['ip', 'route'])
Split_Result = IP.split()

# print Split_Result[2] # Remove "#" to enable

pynotify.init("image")
notify = pynotify.Notification("Ip", "Server Running At:" + Split_Result[2] , "/home/User/wireless.png")    
notify.show()    

The advantage of this is that you don't need to specify the network interface. That's pretty useful when running a socket server

You can install PyNotify using easy_install or even Pip:

easy_install py-notify

or

pip install py-notify

or within python script/interpreter

from pip import main

main(['install', 'py-notify'])
Garwin answered 28/11, 2013 at 21:37 Comment(0)
B
4

netifaces is available via pip and easy_install. (I know, it's not in base, but it could be worth the install.)

netifaces does have some oddities across platforms:

  • The localhost/loop-back interface may not always be included (Cygwin).
  • Addresses are listed per-protocol (e.g., IPv4, IPv6) and protocols are listed per-interface. On some systems (Linux) each protocol-interface pair has its own associated interface (using the interface_name:n notation) while on other systems (Windows) a single interface will have a list of addresses for each protocol. In both cases there is a protocol list, but it may contain only a single element.

Here's some netifaces code to play with:

import netifaces

PROTO = netifaces.AF_INET   # We want only IPv4, for now at least

# Get list of network interfaces
# Note: Can't filter for 'lo' here because Windows lacks it.
ifaces = netifaces.interfaces()

# Get all addresses (of all kinds) for each interface
if_addrs = [netifaces.ifaddresses(iface) for iface in ifaces]

# Filter for the desired address type
if_inet_addrs = [addr[PROTO] for addr in if_addrs if PROTO in addr]

iface_addrs = [s['addr'] for a in if_inet_addrs for s in a if 'addr' in s]
# Can filter for '127.0.0.1' here.

The above code doesn't map an address back to its interface name (useful for generating ebtables/iptables rules on the fly). So here's a version that keeps the above information with the interface name in a tuple:

import netifaces

PROTO = netifaces.AF_INET   # We want only IPv4, for now at least

# Get list of network interfaces
ifaces = netifaces.interfaces()

# Get addresses for each interface
if_addrs = [(netifaces.ifaddresses(iface), iface) for iface in ifaces]

# Filter for only IPv4 addresses
if_inet_addrs = [(tup[0][PROTO], tup[1]) for tup in if_addrs if PROTO in tup[0]]

iface_addrs = [(s['addr'], tup[1]) for tup in if_inet_addrs for s in tup[0] if 'addr' in s]

And, no, I'm not in love with list comprehensions. It's just the way my brain works these days.

The following snippet will print it all out:

from __future__ import print_function  # For 2.x folks
from pprint import pprint as pp

print('\nifaces = ', end='')
pp(ifaces)

print('\nif_addrs = ', end='')
pp(if_addrs)

print('\nif_inet_addrs = ', end='')
pp(if_inet_addrs)

print('\niface_addrs = ', end='')
pp(iface_addrs)

Enjoy!

Beeler answered 11/6, 2014 at 19:47 Comment(1)
netifaces really eases life a lot while dealing with this issue.Kongo
C
4

A Python 3.4 version utilizing the newly introduced asyncio package.

async def get_local_ip():
    loop = asyncio.get_event_loop()
    transport, protocol = await loop.create_datagram_endpoint(
        asyncio.DatagramProtocol,
        remote_addr=('8.8.8.8', 80))
    result = transport.get_extra_info('sockname')[0]
    transport.close()
    return result

This is based on UnkwnTech's excellent answer.

Castara answered 6/4, 2016 at 8:51 Comment(2)
I've +1'd, because I liked (and used) the idea, but the syntax isn't wrong.Keesee
@LogicDaemon, thanks. I just fixed the syntax errors btw.Castara
C
4

To get the ip address you can use a shell command directly in python:

import socket, subprocess

def get_ip_and_hostname():
    hostname =  socket.gethostname()

    shell_cmd = "ifconfig | awk '/inet addr/{print substr($2,6)}'"
    proc = subprocess.Popen([shell_cmd], stdout=subprocess.PIPE, shell=True)
    (out, err) = proc.communicate()

    ip_list = out.split('\n')
    ip = ip_list[0]

    for _ip in ip_list:
        try:
            if _ip != "127.0.0.1" and _ip.split(".")[3] != "1":
                ip = _ip
        except:
            pass
    return ip, hostname

ip_addr, hostname = get_ip_and_hostname()
Cordero answered 3/6, 2016 at 15:48 Comment(1)
Note that ifconfig is deprecated -- even back when this answer was written in 2016, it didn't support all available kernel socket and address types, and could silently hide things (like secondary addresses that aren't bound to named aliases) that the newer iproute2 tools (such as ip addr list) can show.Gharry
C
4
import netifaces as ni 

ni.ifaddresses('eth0')
ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
print(ip)

This will return you the IP address in the Ubuntu system as well as MacOS. The output will be the system IP address as like my IP: 192.168.1.10.

Canker answered 28/12, 2017 at 9:19 Comment(1)
netifaces is not part of the standard library and a very similar answer was already posted.Thirtytwomo
E
3

For a list of IP addresses on *nix systems,

import subprocess
co = subprocess.Popen(['ifconfig'], stdout = subprocess.PIPE)
ifconfig = co.stdout.read()
ip_regex = re.compile('((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?))')
[match[0] for match in ip_regex.findall(ifconfig, re.MULTILINE)]

Though it's a bit late for this answer, I thought someone else may find it useful :-)

PS : It'll return Broadcast addresses and Netmask as well.

Elwira answered 24/2, 2011 at 23:31 Comment(4)
FWIW, I find hostname -i and hostname -I (note the capital i) an easier alternative to ifconfig. The capital version returns all addresses, while the lower case returns the "default", which may be 127.0.1.1 (i.e. useless)Brake
hostname -I (the one with the capital I) is not available in older versions of various operating systems. For example, CentOS 5.2. So, I guess the above script should be preferred to be on the safe side. PS : Thanks for the comment. The command is helpful for latest OS versions.Elwira
Of note, the use of hostname as suggested by Rob is Linux specific. Solaris, for instance, will happily change your hostname to "-I" if you invoke the command given as root.Backwater
Thank you for that note @EliHeady , that save million lives :DMusaceous
M
3

I had to solve the problem "Figure out if an IP address is local or not", and my first thought was to build a list of IPs that were local and then match against it. This is what led me to this question. However, I later realized there is a more straightfoward way to do it: Try to bind on that IP and see if it works.

_local_ip_cache = []
_nonlocal_ip_cache = []
def ip_islocal(ip):
    if ip in _local_ip_cache:
        return True
    if ip in _nonlocal_ip_cache:
        return False
    s = socket.socket()
    try:
        try:
            s.bind((ip, 0))
        except socket.error, e:
            if e.args[0] == errno.EADDRNOTAVAIL:
                _nonlocal_ip_cache.append(ip)
                return False
            else:
                raise
    finally:
        s.close()
    _local_ip_cache.append(ip)
    return True

I know this doesn't answer the question directly, but this should be helpful to anyone trying to solve the related question and who was following the same train of thought. This has the advantage of being a cross-platform solution (I think).

Muniment answered 26/4, 2012 at 0:32 Comment(0)
A
3

Note: This is not using the standard library, but quite simple.

$ pip install pif

from pif import get_public_ip
get_public_ip()
Alemannic answered 30/8, 2013 at 23:9 Comment(1)
the questions was about finding the IP using stdlibPenultimate
N
2

Here are two solutions, I tried to keep them very simple, and to explain each step.

TLDR;

First solution:

import socket, os
hostname = os.uname()[1]
print(socket.gethostbyname_ex(hostname)[2])

Second solution:

import socket
hostname = socket.gethostname()
print(socket.gethostbyname_ex(hostname)[2])

On my machine, both codes return a list containing the IP addresses:

['127.0.1.1', '192.168.1.47']

Detailed explanation:

First, get the hostname of the local machine by using uname function from the os module:

import os
hostname = os.uname()[1]
print(hostname)                      # Just to display an output

Note that this can also be done by calling the gethostname function from the socket module:

import socket
hostname = socket.gethostname()
print(hostname)                      # Just to display an output

Now, when passing this hostname to the gethostbyname_ex function from the socket module, it returns a tuple, the third part of that tuple is a list of the IP addresses:

addresses = socket.gethostbyname_ex(hostname)[2]
print(addresses)                      # Just to display an output

Note that this was largely inspired by a combination of answers above; I just tried to simplify things and make them as clear as possible.

News answered 18/2, 2023 at 19:30 Comment(0)
K
1

Ok so this is Windows specific, and requires the installation of the python WMI module, but it seems much less hackish than constantly trying to call an external server. It's just another option, as there are already many good ones, but it might be a good fit for your project.

Import WMI

def getlocalip():
    local = wmi.WMI()
    for interface in local.Win32_NetworkAdapterConfiguration(IPEnabled=1):
        for ip_address in interface.IPAddress:
            if ip_address != '0.0.0.0':
                localip = ip_address
    return localip







>>>getlocalip()
u'xxx.xxx.xxx.xxx'
>>>

By the way, WMI is very powerful... if you are doing any remote admin of window machines you should definitely check out what it can do.

Koph answered 17/4, 2012 at 13:21 Comment(0)
C
1
import socket
socket.gethostbyname(socket.getfqdn())
Capitalism answered 8/6, 2012 at 9:42 Comment(2)
Rather than only post a block of code, please explain why this code solves the problem posed. Without an explanation, this is not an answer.Neves
The accepted answer already mentions this and you did not even try to give more details ons thisPiderit
H
1

This isn't very Pythonic, but it works reliably on Windows.

def getWinIP(version = 'IPv4'):
    import subprocess
    if version not in ['IPv4', 'IPv6']:
        print 'error - protocol version must be "IPv4" or "IPv6"'
        return None
    ipconfig = subprocess.check_output('ipconfig')
    my_ip = []
    for line in ipconfig.split('\n'):
        if 'Address' in line and version in line:
            my_ip.append(line.split(' : ')[1].strip())
    return my_ip

print getWinIP()

Yeah, it's a hack, but at times I don't feel like second-guessing an operating system, and just go ahead and use what's built-in and works.

Hieratic answered 28/4, 2015 at 23:18 Comment(0)
H
1
from netifaces import interfaces, ifaddresses, AF_INET
iplist = [ifaddresses(face)[AF_INET][0]["addr"] for face in interfaces() if AF_INET in ifaddresses(face)]
print(iplist)
['10.8.0.2', '192.168.1.10', '127.0.0.1']
Hy answered 16/6, 2017 at 4:57 Comment(1)
netifaces is not part of the standard library.Trapper
A
0

A machine can have multiple network interfaces (including the local loopback 127.0.0.1) you mentioned. As far as the OS is concerned, it's also a "real IP address".

If you want to track all of interfaces, have a look at the following Python package, see: http://alastairs-place.net/netifaces/

I think you can avoid having gethostbyname return 127.0.0.1 if you ommit the loopback entry from your hosts file. (to be verified).

Allayne answered 23/6, 2011 at 11:2 Comment(1)
how to get that module netifaces ?Dilatometer
W
0

Yet another variant to previous answers, can be saved to an executable script named my-ip-to:

#!/usr/bin/env python

import sys, socket

if len(sys.argv) > 1:
    for remote_host in sys.argv[1:]:
        # determine local host ip by outgoing test to another host
        # use port 9 (discard protocol - RFC 863) over UDP4
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect((remote_host, 9))
            my_ip = s.getsockname()[0]
            print(my_ip, flush=True)
else:
    import platform

    my_name = platform.node()
    my_ip = socket.gethostbyname(my_name)
    print(my_ip)

it takes any number of remote hosts, and print out local ips to reach them one by one:

$ my-ip-to z.cn g.cn localhost
192.168.11.102
192.168.11.102
127.0.0.1
$

And print best-bet when no arg is given.

$ my-ip-to
192.168.11.102
Wongawonga answered 24/4, 2019 at 7:55 Comment(0)
A
0

For a linux env, read the /proc/net/tcp, the second (localaddress) and third (remoteaddress) will give the IPs at hexa format.

Tip: If second column is zeroed (00000000:0000) so its a Listen Port :)

https://github.com/romol0s/python/blob/master/general/functions/getTcpListenIpsByPort.py

https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt

Adapter answered 25/4, 2019 at 21:22 Comment(0)
C
0

Windows solution, Take it or leave it.

gets only the self ip, on the current active wlan[wireless LAN] ie the computer's ip on the (wifi router or network switch).

note: its not the public ip of the device and does not involve any external requests or packages or public apis.

The core idea is to parse the output of shell command: ipconfig, or ifconfig on linux. we are using subprocess to acquire the output.

def wlan_ip():
    import subprocess
    result=subprocess.run('ipconfig',stdout=subprocess.PIPE,text=True).stdout.lower()
    scan=0
    for i in result.split('\n'):
        if 'wireless' in i: #use "wireless" or wireless adapters and "ethernet" for wired connections
            scan=1
        if scan:
            if 'ipv4' in i:
                return i.split(':')[1].strip()
print(wlan_ip())

this is what happens after CMD:'ipconfig' :

we get this output, we captured it in python using subprocess output.

C:\Users\dell>ipconfig

Wireless LAN adapter Wi-Fi:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::f485:4a6a:e7d5:1b1c%4
   IPv4 Address. . . . . . . . . . . : 192.168.0.131
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.0.1

and we parsed the string in python, in a manner that selects the wireless adapter's ip on current network.

Criswell answered 3/12, 2020 at 17:5 Comment(5)
Looks like this code can be simplified by removing the flag variable scan and replacing three if statements by only one: if 'wireless' in i and 'ipv4' in i:Rollicking
@Rollicking no because we are intrested in line which contain ipv4 after finding the first active wireless adapter in our pc. not containing 'wireless' and 'ipv4' simultaneously. have you tried btw? i have tried yours and output was None.Criswell
This is not a pure Python solution as the OP asked.Papyraceous
@ChrisWolf the networking code is written in C or C++ for most OS. so there isnt an actual solution to access the network info via pure python. we can only use the shell/kernel of the respective Operating systems to interface.Criswell
@nikhilswami When I said "pure Python", I should have said, "pure cPython". The point is that doing tricks like calling out to external executables via subprocess is not a "pure cPython" solution.Papyraceous
C
0

You can do this easily on modern *NIX systems that have the iproute2 utility by calling it via subprocess.run() as you can output in JSON with the -j switch and then use the json.loads() module and method to convert that to a python data structure. The following code displays the first non loopback IP address.

import subprocess
import json

ip = json.loads(subprocess.run('ip -j a'.split(),capture_output=True).stdout.decode())[1]['addr_info'][0]['local'] 

print(ip)

Alternativly if you had multiple IP's and wanted to find the IP that would be used to connect to a specific destination you could use ip -j route get 8.8.8.8 like this:

import subprocess 
import json 

ip = json.loads(subprocess.run('ip -j route get 8.8.8.8'.split(),capture_output=True).stdout.decode())[0]['prefsrc']

print(ip)

If your looking for all IP addresses you can iterate through the list of dictionaries returned by ip -j a

import subprocess
import json

list_of_dicts = json.loads(subprocess.run('ip -j a'.split(),capture_output=True).stdout.decode())

for interface in list_of_dicts:
    try:print(f"Interface: {interface['ifname']:10} IP: {interface['addr_info'][0]['local']}")
    except:pass
Carpology answered 28/9, 2021 at 22:31 Comment(0)
M
0

Here is a solution based fatal_error’s answer, but that doesn't rely on a try-except statement.

Tested on macOS, Raspbian OS and Windows 11.

Returns 127.0.0.1 if there is no network connection.

import socket


def get_ip():
    """
    Idea from https://mcmap.net/q/63357/-finding-local-ip-addresses-using-python-39-s-stdlib
    """
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(0)
    # Doesn’t even have to be reachable.
    s.connect_ex(("10.254.254.254", 1))
    ip = s.getsockname()[0]
    ip = "127.0.0.1" if ip == "0.0.0.0" else ip
    s.close()
    return ip


IP = get_ip()
print(IP)
Mezzotint answered 24/11, 2023 at 16:57 Comment(0)
M
-1

pyroute2 is a great library that can be used to obtain not just ip addresses but also gateway information and other useful information. The following code can obtain the ipv4 address of any interface.

from pyroute2 import IPRoute
ip = IPRoute()

def get_ipv4_address(intf):
    return dict(ip.get_addr(label=intf)[0]['attrs'])['IFA_LOCAL']

print(get_ipv4_address('eth0'))
Mullite answered 3/2, 2020 at 23:55 Comment(0)
L
-2

Simple yet sweet!

def getip():

    import socket
    hostname= socket.gethostname()
    ip=socket.gethostbyname(hostname)

    return(ip)
Laevorotatory answered 1/12, 2013 at 14:2 Comment(2)
never did. just muh two centsLaevorotatory
fails as mentioned above on ubuntuValued
P
-2

This is very similar to previously posted answers, but I could not find any with this usage of calls. This is what I use for ipv4. For ipv6 change the '.' in to ':' in

import socket
print next(i[4][0] for i in socket.getaddrinfo(
    socket.gethostname(), 80) if '127.' not in i[4][0] and '.' in i[4][0]);"
Pterosaur answered 6/12, 2016 at 18:12 Comment(0)
K
-3

I settled for using the service and/or API of ipfy: https://www.ipify.org.

#!/usr/bin/env python3
from urllib.request import urlopen


def public_ip():
    data = urlopen('https://api.ipify.org').read()
    return str(data, encoding='utf-8')


print(public_ip())

The response can also be obtained in JSON and JSONP formats.

There's an ipify Python library on Github.

Koran answered 7/8, 2016 at 13:49 Comment(0)
D
-3
import socket
print(socket.gethostbyname(socket.getfqdn()))
Derogative answered 20/7, 2017 at 18:37 Comment(1)
The difference you posted is confusing as it has nothing to do with any IDE. It is interactive Python shell vs. script.Trapper
A
-3

@fatal_error solution should be the accepted answer! this is an implementation of his solution in nodejs in case people need it:

const dgram = require('dgram');

async function get_local_ip() {
    const s = new dgram.createSocket('udp4');
    return new Promise((resolve, reject) => {
        try {
            s.connect(1, '8.8.8.8', function () {
                const ip = s.address();
                s.close();
                resolve(ip.address)
            });
        } catch (e) {
            console.error(e);
            s.close();
            reject(e);
        }
    })
}
Andresandresen answered 14/7, 2021 at 13:19 Comment(1)
I’m not sure that you’ve posted this in the right place, a js solution is out of place on a python question.Member

© 2022 - 2024 — McMap. All rights reserved.