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 machine,
local the
workstation from where the SSH session originates. 1.2.3.150 is the old IP
that I have to free, and 1.2.3.200 is the new IP. 1.2.3.129 is the router IP
and since it's a
/25 network, 1.2.3.128 is the network address.
I'll be using the
ip command from the
iproute package exclusively.
ifconfig must DIE!
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 1.2.3.200/25 dev eth0
local# nc 1.2.3.200 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 1.2.3.200/25 dev eth0
remote# ip addr add 1.2.3.200/32 dev eth0
Note how we're adding the address with a
/32 netmask instead. This makes
it independent of the current primary address.
Now the fun begins. Close the SSH session and log in to the new IP:
remote# exit
local# ssh root@1.2.3.200
And remove the primary IP; yes, you heard me:
remote# ip route
1.2.3.128/25 dev eth0 proto kernel scope link src 1.2.3.150
default via 1.2.3.129 dev eth0
remote# ip addr del 1.2.3.150/25 dev eth0
remote# ip route
default via 1.2.3.129 dev eth0
Gotta love Linux: it saved the default route because it was still reachable
from the
1.2.3.200/32 address, and only removed the link-local route.
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 1.2.3.200/25 brd 1.2.3.255 dev eth0
remote# ip addr del 1.2.3.200/32 dev eth0
remote# ip route
1.2.3.128/25 dev eth0 proto kernel scope link src 1.2.3.200
default via 1.2.3.129 dev eth0
After modifying
/etc/network/interfaces, the deed is done. The next time
I do this I might consider a safety net of the form:
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 1.2.3.150/25 dev eth0; \
ip addr add 1.2.3.200/25 dev eth0; \
ip route add default via 1.2.3.128
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
tty).
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.