IPv6 works, DHCP renews fine, ARP answers, and not one IPv4 packet gets through

A two-minute link flap on the WAN port, and afterwards IPv4 was dead in both directions while IPv6 was untouched. Every check on the router said healthy. The ISP gateway had quietly taken the public IP back onto itself while still handing that same IP to the router over DHCP.

Symptom

The router is a MikroTik running RouterOS 7. The ISP box is an AT&T BGW fiber gateway in IP Passthrough mode: it leases its own public IPv4 to the router over DHCP, so the router holds the public address directly. IPv6 does not go through passthrough; it is plain routing plus DHCPv6-PD. Those two facts matter below.

On 2026-08-26 the WAN port flapped at 21:07:45 and came back at 21:09:00 (link-downs went from 4 to 6). After that:

Observation What it says
IPv6 egress completely normal, traceroute goes straight out Physical link and ISP are fine
ARP for the IPv4 gateway is complete / reachable Layer 2 is up, the BGW is answering ARP
/ip dhcp-client renew, and release + renew, both succeed, same IP, lease refreshed DHCPv4 traffic passes, the BGW is still handing out the address
ping to the gateway, to any public IPv4, traceroute: all time out Not one IPv4 packet is forwarded outbound
From an outside host, ping to the public IP works, but curl :80 and :443 fail Something is answering for that IP, and it is not us
The dstnat port-forward rule counter: 0 packets Nothing inbound reaches the router either

Why it is easy to misdiagnose

Every signal on the router itself looks healthy. The interface is up, ARP resolves, DHCP renews and the lease is refreshed with the same address. That combination screams “router problem”, so the first hour went into router-side actions: renew, release + renew, staring at routes and firewall rules. None of it could help, and the lease alone explains why: the ISP hands out a 14-week lease, so RouterOS would never re-request on its own anyway.

The one signal that should have short-circuited all of this was the external probe: the public IP answered ping from outside but refused TCP. The router was not answering that ping. The BGW was.

There was also a self-healing hook (netwatch calling a script) that was supposed to handle exactly this. It had never fired once since it was created, for an unrelated reason: the script’s policy list contained policy, and RouterOS silently refuses to run such a script from netwatch. Even fixed, it could not have helped here, see the end of the Fix section.

Root cause

After the flap the BGW re-established its own WAN session and kept the public IPv4 for itself, while its passthrough side continued to lease that same IPv4 to the router. Both devices now believed they owned the address. The BGW answered ARP and ICMP for it upstream and downstream, and forwarded nothing in either direction. IPv6 was unaffected because it never depended on passthrough.

Proof

Two checks, both from the router, decide it:

/ip firewall nat reset-counters
# then from a host outside this network: curl -m 5 http://<public-ip>/
/ip firewall nat print stats where comment~"Forward"

Counter stays at 0 packets: the inbound request never reached the router, so whoever answered the external ping is in front of it. Combined with the successful DHCP renew, the only device that fits is the BGW.

One trap on the way: /ip firewall filter reset-counters did not actually zero the counters on this RouterOS build. /ip firewall nat reset-counters works. Use the NAT one for this test.

Fix

Emergency path first, because the real fix needs either the BGW’s web UI or a phone app.

The config already had a static default route via the BGW’s private LAN address, permanently inactive because there was no on-link route to it. Adding a /32 route is not enough: the source address would still be the public IP, which the BGW ignores. What works is giving the WAN port an address in the BGW’s own private subnet:

/ip address add address=192.168.3.2/24 interface=ether1 comment="BGW mgmt access"

Two things happen at once: the BGW becomes reachable (its web UI answers), and the static default route flips to active, so outbound IPv4 comes back through the BGW’s ordinary NAT.

But now there are two default routes at distance 1, the static one and the DHCP one, and RouterOS load-balances across them (both show + in /ip route print). Half of the flows go into the black hole: 8.8.8.8 works, 9.9.9.9 dies, the same destination succeeds and fails in turns. Move the DHCP route out of the way in the same breath:

/ip dhcp-client set [find interface="ether1"] default-route-distance=10
/ip dhcp-client renew [find interface="ether1"]

This is double NAT. Every inbound port forward is dead while it is in place. It buys time, nothing more.

The real fix is to make the BGW re-run passthrough, from easiest to hardest:

  1. Restart the gateway from the ISP’s mobile app. This is what fixed it on 2026-08-26.
  2. BGW web UI: Restart, or toggle IP Passthrough off, save, on, save. Needs the emergency address above and the Device Access Code printed on the unit.
  3. Pull the power for about 30 seconds.

After the restart: the public IPv4 was unchanged, the IPv6 prefix was unchanged (no LAN renumbering), and every inbound port answered again from outside. The /ip cloud “Router is behind a NAT” warning cleared after force-update.

Then roll the emergency path back, otherwise the static route (distance 1) keeps winning over the DHCP route (distance 10) and port forwards never return:

/ip address remove [find comment~"BGW mgmt access"]
/ip dhcp-client set [find interface="ether1"] default-route-distance=1
/ip dhcp-client renew [find interface="ether1"]

Remove the address first, then restore the distance. The other order opens a window with both routes at distance 1, which is the ECMP black hole again.

Lessons