Example of Linux (Debian) DHCP client hook script
linux has started naming interfaces differently
|Deletions are marked like this.||Additions are marked like this.|
|Line 128:||Line 128:|
| Linux calls all ethernet interfaces `eth`, unlike BSD. It uses a different nomenclature for wireless interfaces, though. Here, notice the two IPv4 (`inet`) and the IPv6 (`inet6`) addresses on `eth0`.
<<BR>><<BR>> Note here that the `ifconfig` command was not even used. Debian and many other OSes based on Linux are moving to the `ip` command, even though it's not in the base system of Debian just yet.
| Linux during this time period began all ethernet interface names with `eth`, unlike BSD systems, where the interface name is the driver name plus a number. Linux uses a different nomenclature for wireless interfaces, though. Here, notice the two IPv4 (`inet`) and the IPv6 (`inet6`) addresses on `eth0`.
<<BR>><<BR>> Note here that the `ifconfig` command was not even used. Debian and many other OSes based on Linux are moving to the `ip` command.
|Line 165:||Line 165:|
* Debian 9.x:
Starting with this version, Debian no longer includes `ifconfig` in the default installation; only `ip`. Also, interface names are created using [[https://www.debian.org/releases/stretch/amd64/release-notes/ch-whats-new.en.html#new-interface-names|systemd predictable names]] by default, although there are ways to return to the old eth0, eth1, etc.
What's My IP Address
One of the most frustrating questions we get in #bash is "Help me extract my IP address from the output of ifconfig." This is a classic XyProblem of the worst kind. People often don't even realize it's a bad question, and then when we try to lead them in a more productive direction, they can't understand why we won't answer the question that they think is so straightforward.
This page, although still incomplete, attempts to give some insight into the multiple layers of complexity that lie behind this deceptively simple question.
Why Do You Care?
Sure, we could tell you one of the hundreds of ways to parse the output of ifconfig using sed or awk or cut or parameter expansions or a plethora of similar tools. But that only helps you insofar as parsing ifconfig output actually makes any sense for achieving your real goal. Without knowing what that goal is, we can't go straight to the heart of the problem, as we would like.
Here is just a small sample of the reasons we've been able to get people to confess, when they asked us this question. There were doubtless many other goals that went undiscovered.
- I have a laptop, and I want to configure routing differently when I plug it in at home vs. when I plug it in at work.
I have a legacy Unix system that gets its primary interface's IP address through DHCP, with no hooks for running scripts with the DHCP client-supplied information. I have to manually construct an /etc/hosts with the address.
- I run a Linux system at home, and I have to send my (DHCP-supplied) IP address to a dynamic DNS service provider to update my A/MX records.
- I'm trying to set up iptables, and then I want to copy this configuration to a dozen more systems, without changing it for every one.
- I work for someone that wants to run a script on 4000 personal digital video recorder boxes in other people's homes. I want to collect information from these boxes and log it somewhere, with the logs identified by the client box's IP address.
Let's look at some of these issues....
My Laptop, It Moves
Most Linux distributions these days have some sort of mechanism for configuring your network interface, routing table, resolv.conf file, etc. from one of multiple sets of information depending on where you boot the laptop. You should use your OS-provided hooks for setting this stuff up. Why are you reinventing the wheel?
My Address, It Changes
DHCP is a pain in the ass for Unix clients. Unix systems were designed to run services and make them available on the network. If your address keeps changing, how are you supposed to serve anyone?
If you control the DHCP server, it becomes an order of magnitude easier. You can assign an IP address to a specific MAC address, so that your Unix system always comes up with the same IP address. (If you don't control the DHCP servers at work, you might be able to request such a designation from the people who do.) If you control the DHCP server and the DNS server, you can have the DHCP server tell the DNS server what public address it just assigned to your host.
If you need to submit your IP address to a dynamic DNS server directly from the client host, then yes, you will need to have the address available. Usually there will be some hook provided by your DHCP client program that provides the address it just received to a program of your choice, so you don't have to try to reverse engineer the assignment that just took place.
If you choose to reinvent the wheel and grab the first IP address you stumble across from ifconfig in an rc.local script, you still run into another problem: it's permissible for a DHCP server to assign you a different address when your lease on the first one expires. Your DHCP client could (in theory, anyway) change your IP address while you're still using the old one. Now, in practice, if this happens it's going to hurt. You can ameliorate the pain slightly, at least, by not just running a hack script one time in rc.local. Instead, use the hooks provided by your DHCP client. Whenever it changes the IP address, it should have some way of running a program to clean things up. Put your code there.
Here's an example, from Debian 8.x:
$ cat /etc/dhcp/dhclient-exit-hooks.d/update-dns #!/bin/sh if [ "$new_ip_address" ] && [ "$new_ip_address" != "$old_ip_address" ]; then /usr/local/sbin/dync "$new_ip_address" fi
Notice how you do not have to get the IP address by parsing some random utility's output. It's provided to you directly in an environment variable.
My Box, It's Old
Shut up, greycat. Nobody cares about your HP-UX systems.
My Firewall, It's A Template
If you want to set up netfilter (iptables) in such a way that the configuration works on a whole bunch of systems, just use the interface name (eth0) instead of the IP address in the rules.
My Company, It's Evil
I wasn't making up the guy who wanted to log information from 4000 "STBs" (set-top boxes -- that was his word for them) in people's homes. That motherfucker went directly onto the /ignore list.
But... But... But...
There is still a fundamental problem at the conceptual level here. Your computer is not likely to have "an IP address". If your computer only has one IP address, then you know a priori that it's 127.0.0.1, so you don't need to parse anything. You also won't care, because you're only talking to yourself.
If you have some sort of hardware interface to a network that speaks the Internet Protocol (ethernet, token ring, wireless alphabet soup, whatever), then you can have one or more IP addresses of two different kinds on each interface. It is perfectly plausible to have a system with a loopback interface, two hardware ethernet interfaces, two IPv4 addresses on the first hardware interface, and an IPv6 address on each hardware interface.
Moreover, there is no standard way to find out what those addresses are. None. Nada. Zip. Any tool you come up with to spit out this information is OS-specific -- even ifconfig.
Let's look at some of those, shall we?
- OpenBSD 4.7:
$ ifconfig -aA lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33160 priority: 0 groups: lo inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4 nfe0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 lladdr 00:26:18:56:6d:2f priority: 0 media: Ethernet autoselect (100baseTX full-duplex) status: active inet 192.168.2.1 netmask 0xffffff00 broadcast 192.168.2.255 inet6 fe80::226:18ff:fe56:6d2f%nfe0 prefixlen 64 scopeid 0x1 inet 192.168.3.1 netmask 0xffffff00 broadcast 192.168.3.255 msk0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 lladdr 00:21:91:19:97:63 priority: 0 media: Ethernet autoselect (100baseTX full-duplex) status: active inet6 fe80::221:91ff:fe19:9763%msk0 prefixlen 64 scopeid 0x2 enc0: flags=0<> mtu 1536 priority: 0 pppoe0: flags=8851<UP,POINTOPOINT,RUNNING,SIMPLEX,MULTICAST> mtu 1492 priority: 0 dev: msk0 state: session sid: 0x692 PADI retries: 76 PADR retries: 0 time: 11d 15:51:00 sppp: phase network authproto pap groups: pppoe egress inet6 fe80::226:18ff:fe56:6d2f%pppoe0 -> prefixlen 64 scopeid 0x5 inet 188.8.131.52 --> 184.108.40.206 netmask 0xffffffff pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33160 priority: 0 groups: pflog
Notice the routable ("real Internet") IPv4 (inet) address on pppoe0 and the two non-routable (Local Area Network, LAN) IPv4 addresses on nfe0. Also note that the names of the interfaces on a BSD system are derived from the name of the driver that controls the interface. They could be xl or nfe or vr or dozens of others. Also notice the three IPv6 (inet6) addresses.
- Debian 5.0:
$ ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000 link/ether 00:0f:ea:47:15:da brd ff:ff:ff:ff:ff:ff inet 192.168.3.5/24 brd 192.168.3.255 scope global eth0:1 inet 192.168.2.5/24 brd 192.168.2.255 scope global eth0 inet6 fe80::20f:eaff:fe47:15da/64 scope link valid_lft forever preferred_lft forever
Linux during this time period began all ethernet interface names with eth, unlike BSD systems, where the interface name is the driver name plus a number. Linux uses a different nomenclature for wireless interfaces, though. Here, notice the two IPv4 (inet) and the IPv6 (inet6) addresses on eth0.
Note here that the ifconfig command was not even used. Debian and many other OSes based on Linux are moving to the ip command.
It's also interesting to note that the display of the IP-aliased addresses (the two IPv4s on eth0) does not align with the way IP aliasing is defined on a Debian system, in the interfaces(5) file:
# ... some stuff has been omitted ... allow-hotplug eth0 iface eth0 inet static address 192.168.2.5 netmask 255.255.255.0 gateway 192.168.2.1 auto eth0:1 iface eth0:1 inet static address 192.168.3.5 netmask 255.255.255.0 up iptables -A OUTPUT -t mangle -m owner --uid-owner 1007 -j TOS --set-tos 0x02
It's even more interesting (to me at least) to note that the order of the IPv4 addresses is not "eth0 first, then eth0:0 and so on". The primary address showed up last. (The manual does not specify this, so don't assume anything.)
- Debian 7.x:
$ ip -o addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 1: lo inet 127.0.0.1/8 scope host lo 1: lo inet6 ::1/128 scope host \ valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether 78:ac:c0:a9:92:64 brd ff:ff:ff:ff:ff:ff 2: eth0 inet 192.168.2.6/24 brd 192.168.2.255 scope global eth0 2: eth0 inet 192.168.3.6/24 brd 192.168.3.255 scope global eth0:1 2: eth0 inet6 fe80::7aac:c0ff:fea9:9264/64 scope link \ valid_lft forever preferred_lft foreverOr, if you just want the IPv4 addresses:
$ ip -o -4 addr show 1: lo inet 127.0.0.1/8 scope host lo 2: eth0 inet 192.168.2.6/24 brd 192.168.2.255 scope global eth0 2: eth0 inet 192.168.3.6/24 brd 192.168.3.255 scope global eth0:1
- Debian 9.x:
Starting with this version, Debian no longer includes ifconfig in the default installation; only ip. Also, interface names are created using systemd predictable names by default, although there are ways to return to the old eth0, eth1, etc.
- HP-UX 10.20:
$ netstat -in Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll ni0* 0 none none 0 0 0 0 0 ni1* 0 none none 0 0 0 0 0 lo0 4608 127 127.0.0.1 73705400 0 73705400 0 0 lan0 1500 10.76.172.0 10.76.173.78 78729542 14 77788491 0 0 $ ifconfig lan0 lan0: flags=863<UP,BROADCAST,NOTRAILERS,RUNNING,MULTICAST> inet 10.76.173.78 netmask fffffe00 broadcast 10.76.173.255
HP-UX uses lan in much the same way Linux uses eth, at least for the interfaces that ship with their workstations. I don't have access to any HP-UX systems that are using IP aliasing, so unfortunately I can't give any better examples.
Of note here is the fact that there's no ifconfig -a analog to show all the information at once. You must get the interface names somehow (netstat -i and variants are handy there), and then pass them to ifconfig one by one if you want details.
$ ifconfig -a ifconfig: no such interface
In addition to the OS-specific tool issue, there is Network Address Translation. The address you can see may not be the address by which other computers can reach you. If you have a non-routable LAN IP address, but you can surf, then your packets are probably going through a NAT firewall. The web server sees your data coming from the public interface of the NAT gateway; it doesn't see your LAN IP address at all. Likewise, if your router is set up to redirect packets to you, then people who connect to you do so through the NAT's public interface. They won't even know there's a NAT involved in the picture.
So, when you ask "What's my IP address", do you really mean "What's my LAN IP address", or do you mean "What's my publically visible IP address"? If you mean the latter, there is no conceivable way you can get that information from your local computer. Your local computer doesn't have that information. It only knows the private (LAN) interface of the NAT gateway, not the public interface. If you want to know the public IP, you have to ask some other system that you trust, which is outside of your LAN, to tell you what address it sees when you connect to it.
There are a slew of crappy web sites that provide that information as a service. Most of them bury it in several kilobytes of useless HTML. Try http://wooledge.org/myip.cgi or http://ifconfig.me/ip instead, or set up your own CGI that does the same thing. It's incredibly trivial. Here it is, in its entirety:
cyclops:~$ cat /var/www/htdocs/wooledge.org/myip.cgi #!/bin/sh echo "Content-type: text/plain" echo echo "$REMOTE_ADDR"
It could be made even shorter using printf.