
Last Update: April 6, 2026
BY
eric
Keywords
The Goal
We have an old Cisco 1941/K9 sitting on the shelf as a backup router. The main router — another Cisco 1941 — is handling NAT, DHCP, and internet access for a home office LAN on 192.168.8.0/24. The ISP provides both IPv4 (DHCP) and IPv6 (native, with a /48 delegation).
It's important to note that the Cisco 1941 is now quite outdated hardware and is no longer supported by Cisco. If your budget allows, you should definitely look into upgrading to a newer model. However, for a home office environment, if managed properly, these devices are absolute warhorses. They remain relatively safe, highly stable, and can still perform significantly better than most consumer-grade routers currently on the market.
The plan:
- Access the backup router's console via a serial cable from an Alpine Linux machine (
alpine) - Pull the running config from the main router
- Add IPv6 (SLAAC + DHCPv6 for LAN clients)
- Push the full config to the backup router over serial
- Verify internet works through the backup router before doing the physical swap
The Setup
ISP (IPv4 DHCP + IPv6 /48)
|
[Backup Cisco 1941] ← Gi0/1 (WAN)
192.168.8.1 ← Gi0/0 (LAN)
|
[alpine - Alpine Linux] ← eth1 (192.168.8.2, connected to backup router)
192.168.8.99 ← eth0 (connected to main router)
|
[Main Cisco 1941]
192.168.8.1
alpine also has a DB9 serial cable going to the backup router's console port (/dev/ttyS0).
Step 1: Console Access via Serial
Modern SSH clients don't support SSHv1, so until we configure SSHv2, serial console is our only way in. Alpine Linux has screen which works perfectly for this.
# Start a detached screen session on the console port
sudo screen -dmS cisco /dev/ttyS0 9600
# Attach to it
sudo screen -r cisco
The Cisco console defaults are 9600 baud, 8N1 — no need to change anything.
Sending a Break Signal for ROMmon
Getting to rommon> requires sending a break signal within 60 seconds of power-on. The trick is to automate this so timing isn't an issue — fire the break loop, then power-cycle the router:
# Send break every second for 60 seconds
for i in $(seq 1 60); do
sudo screen -S cisco -X break
sleep 1
done
Power-cycle the router as soon as the loop starts. When it works, you'll see:
rommon 1 >
Step 2: Pulling the Main Router's Config
The main router is running SSHv1 (Cisco IOS default). Modern OpenSSH 8.0+ dropped SSHv1 entirely, so a standard ssh command will fail:
Protocol major versions differ: 2 vs. 1
The fix: run an old Ubuntu 14.04 container which ships with OpenSSH 6.6 — the last version to support SSHv1.
docker run --rm -it --network host ubuntu:14.04 bash
Inside the container:
apt-get update && apt-get install -y openssh-client sshpass
sshpass -p 'yourpassword' ssh -1 -o StrictHostKeyChecking=no \
[email protected] 'show running-config'
Save the output — this is your baseline config to replicate on the backup router.
Step 3: Building the Config with IPv6
The main router config becomes the template. The key additions for IPv6:
Enable IPv6 Routing
ipv6 unicast-routing
ipv6 cef
DHCPv6 Pool for LAN Clients
ipv6 dhcp pool LAN-V6
dns-server 2001:4860:4860::8888
dns-server 2001:4860:4860::8844
domain-name yourdomain.com
WAN Interface (Gi0/1)
interface GigabitEthernet0/1
ip address dhcp
ip nat outside
ip virtual-reassembly in
ipv6 address dhcp
ipv6 address autoconfig default
ipv6 enable
ipv6 dhcp client pd ISP_PREFIX
no shutdown
ipv6 address dhcp— requests a WAN IPv6 address from the ISP via stateful DHCPv6ipv6 address autoconfig default— accepts SLAAC prefix and installs a default route via RAipv6 dhcp client pd ISP_PREFIX— requests a delegated prefix (IA-PD); the ISP assigns a/48(e.g.2403:580c:d9d4::/48) which is bound to the labelISP_PREFIX
If DHCPv6-PD stays in SOLICIT indefinitely, prefix delegation is not enabled on your account. Contact the ISP — ask them to enable "IPv6 Prefix Delegation (IA-PD)" for your service. They will assign you a static prefix (e.g. a
/48). Once enabled it negotiates in seconds.Without PD, the ISP may assign the WAN address from the same
/64as your LAN prefix, causing an "overlapping" conflict in IOS when you try to configure both interfaces. The workaround is to dropipv6 address dhcpfrom the WAN, use onlyipv6 enable, and assign the/64statically to the LAN — but LAN clients won't have IPv6 internet routing until PD is sorted.
LAN Interface (Gi0/0)
interface GigabitEthernet0/0
ip address 192.168.8.1 255.255.255.0
ip nat inside
ip virtual-reassembly in
ipv6 address ISP_PREFIX ::1/64
ipv6 enable
ipv6 nd other-config-flag
ipv6 dhcp server LAN-V6
no shutdown
ipv6 address ISP_PREFIX ::1/64— assigns the first/64from the delegated/48to the LAN interface. With2403:580c:d9d4::/48delegated, this gives the router2403:580c:d9d4::1/64and advertises2403:580c:d9d4::/64to LAN clients via SLAAC. The remaining/64s (2403:580c:d9d4:1::/64,2403:580c:d9d4:2::/64, ...) are available for future VLANs or subnets.ipv6 nd other-config-flag— tells clients to use SLAAC for their address but get DNS/domain via DHCPv6 (stateless DHCPv6)ipv6 dhcp server LAN-V6— serves the DNS info from our pool above
Static IPv6 Default Route
With only ipv6 enable or autoconfig on the WAN interface, you need a static default route so the router knows to send IPv6 traffic via the ISP:
ipv6 route ::/0 GigabitEthernet0/1 FE80::2A2:FF:FEB2:C2
Replace FE80::2A2:FF:FEB2:C2 with your ISP gateway's link-local address — visible via show ipv6 neighbors GigabitEthernet0/1 once the interface is up. If ipv6 address autoconfig default is configured, IOS will install a default route via RA automatically, so the static route is a belt-and-suspenders fallback.
Enable SSH Version 2
While we're at it, fix the SSHv1 problem for the future:
ip domain name yourdomain.com
crypto key generate rsa modulus 1024
ip ssh version 2
Step 4: Pushing Config via Serial
screen's paste buffer drops characters at serial speeds. The most reliable approach is writing directly to the TTY device:
#!/bin/sh
TTY=/dev/ttyS0
stty -F $TTY 9600 cs8 -cstopb -parenb clocal -echo raw
send_line() {
printf '%s\r\n' "$1" > $TTY
sleep 0.8
}
# Authenticate
printf '\r\n' > $TTY; sleep 1
send_line "hello" # console password
send_line "enable"
send_line "cisco2" # enable password
send_line "configure terminal"
# Send config line by line
while IFS= read -r line; do
printf '%s\r\n' "$line" > $TTY
sleep 0.6
done < /home/dev/cisco/delta.cfg
send_line "end"
send_line "write memory"
Key points:
- Use
stty rawto disable line buffering on the serial device - 0.6–0.8 seconds per line is safe at 9600 baud
- Include
exitcommands between sub-modes (e.g. afterinterface,line) to avoid the router getting confused about context - Read the config back with
show running-configto verify
Reading Output Back from the Serial Port
To capture what the router is saying, read from the TTY in the background while sending commands:
sudo cat /dev/ttyS0 > /tmp/router_output.txt &
CAT_PID=$!
# ... send commands ...
kill $CAT_PID
cat /tmp/router_output.txt
Step 5: Verifying the Config
Once applied, check interfaces and IPv6:
show ip interface brief
show ipv6 interface brief
show ip nat statistics
show ip ssh
Expected output after correct configuration:
GigabitEthernet0/0 192.168.8.1 YES manual up up
GigabitEthernet0/1 192.168.1.x YES DHCP up up
And on the LAN client connected to alpine's eth1:
eth1: inet6 2403:580c:ffff:35db:xxxx:xxxx:xxxx:xxxx/64
SLAAC is working when clients automatically pick up a 2403:580c:ffff:35db::/64 address.
Step 6: Fixing Dual-NIC Asymmetric Routing on alpine
With alpine having two NICs on the same subnet (192.168.8.0/24) — one to the main router, one to the backup — Linux routing gets confused. Both are on 192.168.8.0/24, so the kernel uses whichever it prefers (usually eth0) for both.
This means pings sent out eth1 get their replies routed back via eth0 — the backup router never sees the return traffic.
The fix is policy routing — a separate routing table for traffic sourced from eth1:
# Create a routing table for eth1
echo '200 eth1table' >> /etc/iproute2/rt_tables
# Populate it
ip route add 192.168.8.0/24 dev eth1 src 192.168.8.2 table 200
ip route add default via 192.168.8.1 dev eth1 table 200
# Rule: if source is eth1's IP, use table 200
ip rule add from 192.168.8.2 table 200
Verify:
ping -I eth1 -c 3 8.8.8.8
# Should succeed with ~20ms latency
To make it persistent, add to /etc/network/interfaces:
auto eth1
iface eth1 inet dhcp
post-up ip route add 192.168.8.0/24 dev eth1 src 192.168.8.2 table 200
post-up ip route add default via 192.168.8.1 dev eth1 table 200
post-up ip rule add from 192.168.8.2 table 200
pre-down ip rule del from 192.168.8.2 table 200
Step 7: SSH Shortcut on alpine
Add to ~/.ssh/config on alpine so you can reach the backup router with a simple alias:
Host backup-router
HostName 192.168.8.1
User admin
BindAddress 192.168.8.2
KexAlgorithms +diffie-hellman-group14-sha1
HostKeyAlgorithms +ssh-rsa
Ciphers +aes128-cbc
Then just:
ssh backup-router
The BindAddress 192.168.8.2 forces SSH to source from eth1, which the policy routing table picks up and routes correctly through the backup router.
Step 8: Lock Down SSH to LAN Only
Once the router is live on the internet, its public WAN IP (117.20.x.x) is immediately visible to automated scanners. By default, SSH is accessible from anywhere. Lock it down to LAN-only with an access-class:
ip access-list standard SSH_ACCESS
permit 192.168.8.0 0.0.0.255
deny any log
!
line vty 0 4
access-class SSH_ACCESS in
The deny any log is important — it logs every blocked attempt, so you can see show ip access-lists SSH_ACCESS and watch the hit counter climb. Within minutes of the router going live, you'll see external scanners already hitting it.
The router still listens on TCP port 22 from the WAN side (Cisco doesn't close the port), but drops the connection before authentication for any source IP outside 192.168.8.0/24. SSH from inside your LAN works normally.
The Swap
Once verified, the physical swap is straightforward:
- Unplug the main router's WAN and LAN cables
- Plug them into the backup router's Gi0/1 (WAN) and Gi0/0 (LAN)
- The backup router takes over at
192.168.8.1with identical config + IPv6
LAN clients don't notice — same gateway IP, same DHCP pool, same DNS. They'll additionally start getting IPv6 addresses via SLAAC automatically.
Note — give the modem time after the swap. After plugging the WAN cable into the new router,
Gi0/1may showup/upphysically but receive zero DHCP response for several minutes. This is normal — the modem (especially after a reset) needs time to fully come back up and accept a new client. Ifshow dhcp leaseshows statePurgingandDHCP Lease server: 0.0.0.0, just wait 2–5 minutes before troubleshooting further. You can monitor with:show interface GigabitEthernet0/1 | include input rate|Last inputOnce you see
input rateclimbing above 0 andLast inputticking, the modem is responding and a DHCP lease will follow shortly.
Key Takeaways
- SSHv1 routers: use an old Ubuntu 14.04 Docker container with OpenSSH 6.6 — it's the cleanest solution
- Serial config push: write directly to the TTY device, not through
screen's paste buffer — it's more reliable - IPv6 on Cisco IOS:
ipv6 nd other-config-flag+ipv6 dhcp servergives you SLAAC addresses + DHCPv6 DNS info, which is all most LAN clients need - ISP IPv6 PD: if DHCPv6-PD stays in SOLICIT indefinitely, your ISP doesn't have it enabled — contact them and ask for "IPv6 Prefix Delegation (IA-PD)". Once enabled, the ISP assigns a
/48and LAN clients get full IPv6 internet via SLAAC automatically - Dual-NIC same subnet: always use policy routing — two default routes with different metrics is not enough when both NICs share a subnet
- Lock down VTY immediately: apply
access-classto restrict SSH to LAN-only before the router goes live on the internet — scanners hit public IPs within minutes





Comments (0)
Leave a Comment