715

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?

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
UnkwnTech
  • 88,102
  • 65
  • 184
  • 229
  • 11
    The local IP? Or public IP? How are you going to deal with systems with multiple IPs? – Sargun Dhillon Nov 05 '08 at 17:29
  • use `ifconfig -a` and use the output from there... – Fredrik Pihl Jun 23 '11 at 11:18
  • 18
    @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). – phihag Jun 23 '11 at 13:07
  • 14
    @phihag you are absolutely correct, thanks for correcting my stupidity – Fredrik Pihl Jun 24 '11 at 20:16
  • 1
    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, `getsockname`is a good choice. – Pekka Nikander Apr 30 '12 at 04:58
  • What if there's NAT and I want to still use callback to connect to a socket. – Parth Lathiya Apr 30 '22 at 03:22

49 Answers49

625

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.

Zags
  • 37,389
  • 14
  • 105
  • 140
UnkwnTech
  • 88,102
  • 65
  • 184
  • 229
  • 38
    Nice if you have several interfaces on the machine, and needs the one which routes to e.g. gmail.com – elzapp Oct 20 '10 at 14:16
  • 5
    It might be a good idea to catch socket.error exceptions which may be risen by s.connect()! – phobie Oct 14 '11 at 14:52
  • Nice, I want the IP some client saw when connecting to my service anyway. – Olson Apr 15 '12 at 16:15
  • 45
    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. – wobmene Apr 16 '12 at 12:27
  • 12
    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. – Prof. Falken Aug 22 '13 at 08:07
  • 1
    @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! :-) – KyleFarris Sep 20 '13 at 03:06
  • any way to get this to work with broadcast addresses instead of gmail.com? – Jayen Dec 04 '13 at 05:09
  • I like this. I'd like it better if I could figure out how to use the IP address of a vpn tunnel. – Epu Dec 09 '13 at 23:55
  • 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)) – Rui Botelho Apr 03 '15 at 13:21
  • 3
    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. – grim May 11 '15 at 18:29
  • As noted by @grim 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 http://stackoverflow.com/a/6556951/1411638 – Sdlion Sep 17 '15 at 16:24
  • @feisky I just confirmed this also works on OSX (running El Capitan) – Josep Valls Jan 12 '16 at 20:00
  • Clever, in this way one can find the right interface IP to a specify destination host, and without actually sending any packet – adamhj Aug 03 '16 at 01:00
  • To add to the 9 years worth of comments: This solution just worked for me on a standalone processor running Yocto Linux in 2017. – Snesticle Apr 10 '17 at 22:08
  • 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))`. – Niko Föhr Jul 21 '17 at 19:34
  • SOCK_DGRAM (UDP) sockets do not support `connect`. So you should stick to some TCP address – yanpas Sep 20 '17 at 15:41
  • 1
    As I suggested in an [answer](https://stackoverflow.com/a/23822431/748925) over 3 year before this one was last updated, you can replace the specific portname with s.connect(('',0)) but first you need to call s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) – dlm May 04 '18 at 15:42
  • 1
    @yanpas "SOCK_DGRAM (UDP) sockets do not support connect. So you should stick to some TCP address". [Not true](https://docs.oracle.com/cd/E19683-01/816-5042/sockets-14/index.html) – dlm May 04 '18 at 15:46
  • Using '' 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. – dlm May 04 '18 at 15:51
  • 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. – Compl Yue Apr 24 '19 at 07:36
  • In my book hackishness doesn't mean something is bad if it works well and can still be understood. – The Daleks stand with Ukraine Apr 22 '20 at 23:15
  • 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 order – Gerard May 26 '20 at 09:48
  • 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. – Mark Sep 17 '20 at 17:21
  • Amazing! This is the best solution and solves my multi-ip addresses issue on both Linux and Windows – Investing TS May 25 '21 at 13:30
561
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.

alexandreferris
  • 662
  • 1
  • 11
  • 29
Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • 54
    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. – Jason Baker Oct 03 '08 at 12:07
  • 25
    A variation: socket.gethostbyname(socket.getfqdn()) – gimel Oct 03 '08 at 12:08
  • 64
    This appears to only return a single IP address. What if the machine has multiple addresses? – Jason R. Coombs Oct 23 '09 at 14:39
  • 41
    On Ubuntu this returns 127.0.1.1 for some reason. – slikts Mar 20 '12 at 05:52
  • 3
    @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. – amarillion Sep 24 '12 at 08:50
  • Don't depend on DNS (and possibly cause blocking on network I/O) if all you want to know is a local hostname. – Glenn Maynard Feb 28 '13 at 19:17
  • 3
    @amarillion I have found that rabbitmq is unhappy if this line is missing from /etc/hosts – Lorin Hochstein Jun 21 '13 at 02:43
  • 3
    @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. – dotancohen Nov 21 '13 at 15:26
  • 7
    @UnkwnTech please do not accept the answer, it's clearly unreliable. – Federico Jan 07 '15 at 12:41
  • 27
    @Jason R. Coombs, use following code to retrieve list of IPv4 addresses that belong to the host machine: `socket.gethostbyname_ex(socket.gethostname())[-1]` – Barmaley May 15 '15 at 20:36
  • This one works if you know the interface: http://stackoverflow.com/a/24196955/385482 – stanga Jun 09 '15 at 14:59
  • 2
    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 order – TimSmith-Aardwolf Jul 26 '15 at 15:28
  • 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. – Matthias Urlichs Nov 15 '15 at 17:59
  • 1
    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 ip – RAFIQ Nov 20 '15 at 04:45
  • 2
    On Ubuntu 17.10, even @gimel's answer returns "127.0.0.1" – code Apr 11 '18 at 11:40
  • Every use of `socket.getfqdn()` introduces a delay of 8-10 seconds on my platform. (I'm using Windows on a large corporate network.) – JDM Aug 28 '18 at 11:42
  • 2
    return '127.0.0.1' – leon wu Apr 11 '19 at 06:36
  • 1
    This does not return VPN addresses for example – Gerard May 05 '20 at 11:45
  • 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. https://stackoverflow.com/questions/24196932/how-can-i-get-the-ip-address-from-nic-in-python/64530508#64530508 – Geruta Oct 26 '20 at 01:38
  • I'm on an AWS EC2 and had to use a modification of @Barmaley answer: `socket.gethostbyname_ex(socket.gethostname())[-1][-1]` – Chris Wolf Feb 04 '21 at 14:17
  • 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. – aiwl Dec 29 '21 at 12:00
  • This is definitely the most straightforward answer. – TheTridentGuy supports Ukraine May 13 '23 at 04:19
454

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. :)

fatal_error
  • 5,457
  • 2
  • 18
  • 18
  • 11
    Works in Raspbian with Python 2 and 3 ! – pierce.jason Sep 23 '15 at 17:03
  • 3
    Brilliant. Works on Win7,8,8.1 + Linux Mint & Arch, including VMs. – shermy Mar 03 '16 at 00:07
  • 2
    Works in Windows 10 Pro! Thank you, Jamieson Becker! – Valdemar Apr 12 '17 at 14:02
  • 3
    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.04 – Pedro Scarapicchia Junior Apr 15 '17 at 06:36
  • 39
    This should be the accepted answer. `socket.gethostbyname(socket.gethostname())` gives horrible results. – Jason Floyd Oct 15 '18 at 18:19
  • 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. – Rich Andrews Mar 23 '19 at 02:06
  • 1
    @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. – fatal_error Apr 12 '19 at 06:47
  • 1
    Would this work with any [private network](https://en.wikipedia.org/wiki/Private_network) IP? E.g. `10.0.0.0` also works for me. – djvg Dec 16 '20 at 20:40
  • 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) – Chris Wolf Feb 04 '21 at 14:35
  • 1
    agree, this answer is the only one that truly works. I also added this solution implemented in nodejs – Laurens Jul 14 '21 at 13:17
  • @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) – BadCatss Oct 17 '21 at 23:19
  • 1
    @BadCatss 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). – fatal_error Oct 19 '21 at 00:55
  • 1
    @djvg, yes, in general you can use it with any IP address that would be accessed through your default router. – fatal_error Oct 19 '21 at 00:59
  • @ChrisWolf probably because that's where your default gateway is pointing at (into the tunnel) – fatal_error Oct 19 '21 at 01:00
  • @fatal_error I also have an app called "IP Broadcaster" which correctly displays my broadband address even while connected to corporate VPN. https://apps.apple.com/us/app/ip-broadcaster/id520207162?mt=12 It would be interesting to see what they're doing. – Chris Wolf Oct 27 '21 at 23:16
  • Ok, I ran "strings" on "IP Broadcaster" and it's actually calling out to a web service: http://checkip.dyndns.com/ (that's cheating, sort-of) – Chris Wolf Oct 27 '21 at 23:21
  • @ChrisWolf right on :) also see last paragraph in the answer. – fatal_error Oct 28 '21 at 17:15
  • 1
    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. – FreemanX Dec 08 '21 at 07:42
  • @FreemanX excellent suggestion, added to answer! – fatal_error Jan 07 '22 at 18:14
  • 1
    I get a `Permission denied` exception with this, on Ubuntu 22.04 – Bram Jun 14 '22 at 17:17
  • 1
    @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! – fatal_error Jun 20 '22 at 10:23
165

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])

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.

Alexander
  • 9,737
  • 4
  • 53
  • 59
  • `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. – Mark Sep 08 '11 at 03:58
  • 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. – Mark Sep 08 '11 at 04:05
  • 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 too – kdbdallas Dec 04 '11 at 07:25
  • 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. – Alexander Dec 05 '11 at 12:27
  • 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. – Wladimir Palant Oct 01 '13 at 15:02
  • @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. – Alexander Oct 02 '13 at 14:28
  • 3
    @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. – Wladimir Palant Oct 04 '13 at 07:01
  • 1
    @Alexander, `/etc/resolve.conf: No such file or directory` and I have local IPv4 address shown by `ifconfig`. – anatoly techtonik Dec 16 '13 at 16:30
  • 2
    I can confirm that the updated version works with Ubuntu 14.04 with both Python2 and Py3k. – Uli Köhler Jun 06 '14 at 22:55
  • 4
    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. – Peter Hansen Jun 13 '14 at 21:19
  • 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 fine – Chris Feb 04 '16 at 02:07
  • Chris, does it work if you try with port 53 instead? – Alexander Feb 05 '16 at 10:03
  • 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. – starfry Sep 28 '17 at 15:09
  • 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. – Alexander Sep 28 '17 at 19:27
  • 1
    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]]` – ccpizza Oct 22 '17 at 09:47
  • 1
    @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. – Alexander Jan 03 '18 at 11:09
  • The proper way is to use a Python module with IPv6 support, not any of the answers on this page. – Alexander Sep 19 '19 at 08:08
  • 1
    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. – Chris Wolf Feb 04 '21 at 14:25
104

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.

ccpizza
  • 28,968
  • 18
  • 162
  • 169
Dzinx
  • 55,586
  • 10
  • 60
  • 78
  • 24
    As stated in the question I want something from the default install, as in no additional installs needed. – UnkwnTech Oct 03 '08 at 12:52
  • 6
    @MattJoiner Neither of this things is true any more (the latest version has Windows binaries on PyPI and does support Py3K). – al45tair May 02 '14 at 15:42
  • 6
    @Jean-PaulCalderone FWIW, the latest version of netifaces *does* support IPv6 on Windows. – al45tair May 02 '14 at 15:43
  • 3
    this module must be part of the standard library, given that python claims a 'batteries included' philosophy – ccpizza Oct 22 '17 at 08:42
  • raise a bug, maybe it'll get included into `sys` it seems like a good candidate for that. – Jasen Nov 24 '17 at 03:53
  • 1
    @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. – Craig S. Anderson May 30 '19 at 03:59
55

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]
Collin Anderson
  • 14,787
  • 6
  • 68
  • 57
52

Socket API method

see https://stackoverflow.com/a/28950776/711085

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.

ninjagecko
  • 88,546
  • 24
  • 137
  • 145
  • 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. – phihag Jun 23 '11 at 11:10
  • It doesn't create a TCP connection because it creates a UDP connection. – Anuj Gupta Mar 26 '13 at 16:41
  • 2
    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(('', 0)) to avoid DNS lookup. (I guess there might be a problem with a broadcast if there is a firewall) – dlm Aug 12 '13 at 03:55
40

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'
>>> 
tMC
  • 18,105
  • 14
  • 62
  • 98
  • 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? – Dave Nov 14 '13 at 18:00
  • 1
    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. https://en.wikipedia.org/wiki/Ioctl http://lxr.free-electrons.com/source/net/socket.c – tMC Nov 14 '13 at 20:04
  • 3
    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)` – pepoluan Jan 03 '14 at 08:28
  • how can i do the same for ipv6? – cfischer Feb 06 '14 at 13:23
  • 2
    @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 - http://man7.org/linux/man-pages/man3/getifaddrs.3.html – tMC Feb 12 '14 at 15:37
  • 1
    @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.html – tMC Apr 02 '14 at 12:57
  • This is nice because it doesn't require an internet connection. – jtpereyda Oct 16 '15 at 23:15
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.

smerlin
  • 6,446
  • 3
  • 35
  • 58
  • 2
    This is incompatible with the new predictable Linux interface names, such as `enp0s25`. For more info, see https://wiki.archlinux.org/index.php/Network_Configuration#Device_names – Alexander Apr 25 '14 at 11:54
  • 3
    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: http://stackoverflow.com/q/27391167/76010 – Bakanekobrain Mar 06 '15 at 00:09
  • 1
    on Raspbian w/ Python 2.7.3 - bytes() did not like the 2nd argument. But this worked: `struct.pack('256s', bytes(ifname[:15]))` – colm.anseo Oct 17 '15 at 18:57
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.

silviot
  • 4,615
  • 5
  • 38
  • 51
Dzinx
  • 55,586
  • 10
  • 60
  • 78
  • The one liner solution above generally works on windows. It's the Linux one that's being a problem. – ricree Jun 18 '09 at 00:19
  • 15
    +1 This technique at least attempts to return all addresses on the machine. – Jason R. Coombs Oct 23 '09 at 14:42
  • 1
    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. – Jason R. Coombs Oct 23 '09 at 14:43
  • 1
    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.contents – Jason R. Coombs Oct 23 '09 at 16:09
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())
dlm
  • 4,054
  • 2
  • 23
  • 19
23

I use this on my ubuntu machines:

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

This doesn't work.

martinarroyo
  • 9,389
  • 3
  • 38
  • 75
shino
  • 4,562
  • 5
  • 38
  • 57
18

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())
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
www-0av-Com
  • 707
  • 10
  • 14
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'
linusg
  • 6,289
  • 4
  • 28
  • 78
Graham Chap
  • 322
  • 2
  • 9
  • 3
    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' – cessor Jul 28 '18 at 13:34
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'])
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
Eli Collins
  • 8,375
  • 2
  • 34
  • 38
  • I thought this could have been a really good answer.. but it always return `None` – Jamie Lindsey Jan 27 '20 at 16:31
  • @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. – Eli Collins Jan 28 '20 at 20:07
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.

Jason Baker
  • 192,085
  • 135
  • 376
  • 510
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.

gavaletz
  • 337
  • 2
  • 6
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.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
viker
  • 89
  • 1
  • 1
  • 1
    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. – Helmut Grohne Apr 09 '13 at 13:02
  • 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. – Dominik George Sep 12 '17 at 07:42
6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Nakilon
  • 34,866
  • 14
  • 107
  • 142
  • 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...)... – bryn Nov 20 '13 at 13:30
  • 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. – Nakilon Jul 19 '14 at 03:48
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()
fccoelho
  • 6,012
  • 10
  • 55
  • 67
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())
WolfRage
  • 51
  • 1
  • 2
  • 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. – Nicolas Defranoux Jan 21 '15 at 11:47
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.

hmofrad
  • 1,784
  • 2
  • 22
  • 28
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)]
Ben Last
  • 801
  • 8
  • 6
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'])
DarkXDroid
  • 311
  • 2
  • 11
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!

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.

Frederik Aalund
  • 1,324
  • 2
  • 14
  • 17
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()
RiccardoCh
  • 1,060
  • 1
  • 13
  • 24
  • 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. – Charles Duffy Nov 29 '20 at 19:58
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.

shinjw
  • 3,329
  • 3
  • 21
  • 42
Ishwarya
  • 41
  • 2
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.

phihag
  • 278,196
  • 72
  • 453
  • 469
  • 3
    He means his externally visible IP. The 127.*.*.* range typically refers to localhost or an internal network, which is clearly not what he wants. – Cerin May 17 '12 at 18:59
3

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

$ pip install pif

from pif import get_public_ip
get_public_ip()
Artur Barseghyan
  • 12,746
  • 4
  • 52
  • 44
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.

Kulbir Saini
  • 3,926
  • 1
  • 26
  • 34
  • 1
    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) – RobM Apr 04 '11 at 18:02
  • 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. – Kulbir Saini Apr 30 '11 at 06:13
  • 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. – Eli Heady Nov 23 '11 at 01:19
  • Thank you for that note @EliHeady , that save million lives :D – Phyo Arkar Lwin Feb 28 '12 at 14:10
2

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).

Etienne Perot
  • 4,764
  • 7
  • 40
  • 50
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.

1
import socket
socket.gethostbyname(socket.getfqdn())
Oink
  • 121
  • 1
  • 9
  • 5
    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. – Martijn Pieters Oct 20 '12 at 12:27
  • 1
    The accepted answer already mentions this and you did not even try to give more details ons this – Murmel Nov 22 '15 at 16:20
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.

LRund
  • 19
  • 1
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']
Villiam
  • 57
  • 4
1

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.

Pierre
  • 530
  • 5
  • 13
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
Compl Yue
  • 164
  • 1
  • 3
  • 16
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

Romolo
  • 1
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).

JIST
  • 1,139
  • 2
  • 8
  • 30
ddewaele
  • 22,363
  • 10
  • 69
  • 82
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.

nikhil swami
  • 2,360
  • 5
  • 15
  • 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:` – Georgy Dec 03 '20 at 17:47
  • @Georgy 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`. – nikhil swami Dec 04 '20 at 12:29
  • This is not a pure Python solution as the OP asked. – Chris Wolf Feb 04 '21 at 14:20
  • @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. – nikhil swami Feb 05 '21 at 02:48
  • @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. – Chris Wolf Feb 09 '21 at 23:31
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
TCB919
  • 133
  • 6
-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'))
Josh
  • 9
  • 2
-2

Simple yet sweet!

def getip():

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

    return(ip)
Matt
  • 37
  • 7
-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]);"
Wyrmwood
  • 3,340
  • 29
  • 33
-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.

Apalala
  • 9,017
  • 3
  • 30
  • 48
-3
import socket
print(socket.gethostbyname(socket.getfqdn()))
-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);
        }
    })
}
Laurens
  • 2,596
  • 11
  • 21
  • 2
    I’m not sure that you’ve posted this in the right place, a js solution is out of place on a python question. – UnkwnTech Jul 15 '21 at 14:04