I have a machine in Singapore and have been asked to switch it to another IP address (don't ask...). Due to numerous reasons, I do not want to reboot the machine, so I am faced with the task of switching the primary IP address of a remote host via SSH. Fun.
In the following
remote identifies the remote
local the workstation from where the SSH
session originates. 18.104.22.168 is the old IP that I have to free,
and 22.214.171.124 is the new IP. 126.96.36.199 is the router IP and since
/25 network, 188.8.131.52 is the network
I'll be using the
ip command from the
iproute package exclusively.
The first step of this endeavour is making sure that traffic to your new IP is not going to be caught by the packet filter. This is simple enough:
remote# ip addr add 184.108.40.206/25 dev eth0 local# nc 220.127.116.11 22 SSH-2.0-OpenSSH_4.3p2 Debian-3~bpo.1
We have thus added a secondary address and verified that it can communicate, at least receive packets on port 22. However, since it is a secondary address, it depends on the primary address and will be removed if you remove the primary from the interface. We thus need to get tricky:
remote# ip addr del 18.104.22.168/25 dev eth0 remote# ip addr add 22.214.171.124/32 dev eth0
Note how we're adding the address with a
netmask instead. This makes it independent of the current primary
Now the fun begins. Close the SSH session and log in to the new IP:
remote# exit local# ssh email@example.com
And remove the primary IP; yes, you heard me:
remote# ip route 126.96.36.199/25 dev eth0 proto kernel scope link src 188.8.131.52 default via 184.108.40.206 dev eth0 remote# ip addr del 220.127.116.11/25 dev eth0 remote# ip route default via 18.104.22.168 dev eth0
Gotta love Linux: it saved the default route because it was
still reachable from the
22.214.171.124/32 address, and
only removed the link-local route.
Note that, at that point, the remote host does not belong to any subnet anymore. It may not be able to reach (or be reached by) other hosts from the subnet in which it resided. However, it is still reachable through its default gateway (126.96.36.199). Therefore, if your path to the host is going throught that gateway, you will not experience any connection loss. Hosts on the same subnet (same physical network) might still reach the machine via the gateway too, depending on the latter's filtering configuration (firewall).
We could stop right here for there is no real reason why this machine should have a link-local route or even know about the network to which it is attached; but we won't... instead, we'll add the new address again (a second time), now with a proper netmask:
remote# ip addr add 188.8.131.52/25 brd 184.108.40.206 dev eth0 remote# ip addr del 220.127.116.11/32 dev eth0 remote# ip route 18.104.22.168/25 dev eth0 proto kernel scope link src 22.214.171.124 default via 126.96.36.199 dev eth0
/etc/network/interfaces, the deed
is done. The next time I do this I might consider a safety net of
remote# echo 'ifdown --force eth0; ifup eth0' | at now + 10 minutes
But that would only have been half as fun.
Of course it goes without saying that the above comes without warranties, if you try it out, you do so at your own risk. :)
NP: Neurosis / The Eye of every Storm
Update: It's been proposed to just run:
remote# ip addr del 188.8.131.52/25 dev eth0; \ ip addr add 184.108.40.206/25 dev eth0; \ ip route add default via 220.127.116.11
This may well work, especially if run within a screen session
(I've had times when the shell did not execute subsequent commands
after being cut off from the controlling
However, this method does not guard against typoes. Or did you spot the typo in the above set of commands?
The method I provided above seems resilient to typos as no command ever affects the current connection.