Applies to: KVM VPS in Sofia whose IPv6 default gateway is 2a14:640::1
Affects: IPv6 only. IPv4 is not affected.
Time needed: about 10 minutes
Symptoms
- Roughly 1 in 30 new IPv6 connections (SSH, HTTP, DNS, etc.) hang and time out, and a retry a moment later works.
- Ping over IPv6 usually works, which makes the problem easy to miss.
- In
ip -6 neigh, the gateway2a14:640::1is often shown asFAILED,INCOMPLETEorPROBE, even while traffic is flowing. - You may also see one or two unknown
fe80::routers in stateFAILED.
Cause
The upstream IPv6 gateway 2a14:640::1 forwards traffic normally but rarely answers neighbour solicitations. Neighbour solicitations are how a server looks up the gateway's hardware (MAC) address. The server usually learns the MAC from packets the router sends. When that entry expires and the router doesn't answer, new outgoing connections are dropped for a short time.
The gateway can also send ICMPv6 redirects to other routers that don't answer, which causes similar drops.
The gateway is part of the upstream network and can't be changed from the VPS side. The workaround below makes your server independent of those neighbour solicitations.
1. Check whether you are affected
Run as root (Linux):
ip -6 route show default ip -6 neigh show dev eth0 nstat -az | grep -E 'Icmp6(OutNeighborSolicits|InNeighborAdvertisements|InRedirects)'
You are likely affected if:
- the default route is
via 2a14:640::1, and Icmp6OutNeighborSolicitsis much higher thanIcmp6InNeighborAdvertisements(for example 60 sent, 3 received), and/or- the gateway entry is often
FAILED,INCOMPLETEorPROBE.
Replace eth0 with your interface name if it differs (ip -br link lists them, e.g. ens3).
Optional: measure it. This makes 30 IPv6 TCP connections to a host you control and counts failures. Replace the address and port with your own:
fails=0; for i in $(seq 1 30); do timeout 4 bash -c 'exec 3<>/dev/tcp/2001:db8::10/22' 2>/dev/null || fails=$((fails+1)); sleep 0.5; done; echo "failed: $fails/30"
2. Find the gateway's MAC address
Ping the gateway, then read its entry:
ping -6 -c 3 2a14:640::1 ip -6 neigh show 2a14:640::1 dev eth0
Example output:
2a14:640::1 lladdr e4:f2:7c:b7:21:94 router PROBE
The value after lladdr is the gateway's MAC (e4:f2:7c:b7:21:94 in this example). Use the MAC your own server shows. If no lladdr appears, run the ping again and re-check.
In the commands below, replace:
| Placeholder | Meaning | Example |
|---|---|---|
eth0 |
your network interface | eth0, ens3 |
GATEWAY_MAC |
the MAC from this step | e4:f2:7c:b7:21:94 |
3. Apply the fix now (temporary, until reboot)
ip -6 neigh replace 2a14:640::1 lladdr GATEWAY_MAC dev eth0 nud permanent sysctl -w net.ipv6.conf.all.accept_redirects=0 net.ipv6.conf.default.accept_redirects=0 net.ipv6.conf.eth0.accept_redirects=0 ip -6 neigh flush dev eth0 nud failed
Check:
ip -6 neigh show 2a14:640::1 dev eth0 # expected: 2a14:640::1 lladdr GATEWAY_MAC PERMANENT
If IPv6 works well now, make the fix permanent with the section for your system below.
4. Make it permanent
Ignore IPv6 redirects (all Linux distributions)
cat > /etc/sysctl.d/60-ipv6-no-redirects.conf <<'EOF' net.ipv6.conf.all.accept_redirects = 0 net.ipv6.conf.default.accept_redirects = 0 EOF sysctl --system
Debian / Ubuntu with ifupdown (/etc/network/interfaces)
A systemd unit sets the entry at boot, and an if-up hook sets it again whenever the interface comes up:
cat > /etc/systemd/system/ipv6-gateway-pin.service <<'EOF' [Unit] Description=Pin the IPv6 gateway link-layer address Wants=network-online.target After=network-online.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/sbin/ip -6 neigh replace 2a14:640::1 lladdr GATEWAY_MAC dev eth0 nud permanent ExecStop=-/usr/sbin/ip -6 neigh del 2a14:640::1 dev eth0 [Install] WantedBy=multi-user.target EOF cat > /etc/network/if-up.d/ipv6-gateway-pin <<'EOF' #!/bin/sh [ "$IFACE" = "eth0" ] || exit 0 /usr/sbin/ip -6 neigh replace 2a14:640::1 lladdr GATEWAY_MAC dev eth0 nud permanent || true EOF chmod 755 /etc/network/if-up.d/ipv6-gateway-pin systemctl daemon-reload systemctl enable --now ipv6-gateway-pin.service
Tested on Debian 13 (trixie).
Ubuntu with netplan (systemd-networkd)
Netplan has no neighbour setting, but systemd-networkd can read a drop-in for the file netplan generates. Find that file's name first:
ls /run/systemd/network/ # e.g. 10-netplan-eth0.network
Create a drop-in with the same base name plus .d:
mkdir -p /etc/systemd/network/10-netplan-eth0.network.d cat > /etc/systemd/network/10-netplan-eth0.network.d/ipv6-gateway-pin.conf <<'EOF' [Neighbor] Address=2a14:640::1 LinkLayerAddress=GATEWAY_MAC EOF networkctl reload networkctl reconfigure eth0
Uses the standard [Neighbor] section of systemd.network; confirm on your image before relying on it.
AlmaLinux / Rocky Linux / CentOS Stream (NetworkManager)
A dispatcher script sets the entry every time the interface comes up:
cat > /etc/NetworkManager/dispatcher.d/90-ipv6-gateway-pin <<'EOF' #!/bin/sh [ "$1" = "eth0" ] || exit 0 case "$2" in up|dhcp6-change|reapply) /usr/sbin/ip -6 neigh replace 2a14:640::1 lladdr GATEWAY_MAC dev eth0 nud permanent ;; esac EOF chmod 755 /etc/NetworkManager/dispatcher.d/90-ipv6-gateway-pin systemctl enable --now NetworkManager-dispatcher.service nmcli device reapply eth0
Uses NetworkManager's standard dispatcher mechanism; confirm on your image before relying on it.
Windows Server
Open PowerShell or Command Prompt as Administrator. Find the interface name with netsh interface ipv6 show interfaces. Windows writes MAC addresses with dashes:
netsh interface ipv6 add neighbors "Ethernet" 2a14:640::1 e4-f2-7c-b7-21-94 netsh interface ipv6 set global icmpredirects=disabled netsh interface ipv6 show neighbors "Ethernet" | findstr 2a14:640::1
netsh changes are persistent by default. The neighbour should show as Permanent.
Uses standard netsh commands; confirm on your image before relying on it.
5. Verify
ip -6 neigh show 2a14:640::1 dev eth0 # PERMANENT sysctl net.ipv6.conf.all.accept_redirects # 0 ping -6 -c 3 2606:4700:4700::1111 # 0% packet loss
Repeat the optional 30-connection test from step 1: the failure count should be 0.
Reboot once (when convenient) and confirm the entry is still PERMANENT.
Undo
Debian / Ubuntu (ifupdown):
systemctl disable --now ipv6-gateway-pin.service rm -f /etc/systemd/system/ipv6-gateway-pin.service /etc/network/if-up.d/ipv6-gateway-pin systemctl daemon-reload
Ubuntu (netplan):
rm -r /etc/systemd/network/10-netplan-eth0.network.d networkctl reload ip -6 neigh del 2a14:640::1 dev eth0
AlmaLinux / Rocky:
rm -f /etc/NetworkManager/dispatcher.d/90-ipv6-gateway-pin ip -6 neigh del 2a14:640::1 dev eth0
Windows:
netsh interface ipv6 delete neighbors "Ethernet" 2a14:640::1
To stop ignoring redirects on Linux, delete /etc/sysctl.d/60-ipv6-no-redirects.conf and reboot.
Important notes
- If the upstream router's MAC changes, IPv6 stops working on a server with this fix, while IPv4 and SSH over IPv4 keep working. If IPv6 suddenly fails completely:
- Undo the pin.
- Ping
2a14:640::1again and read the new MAC (step 2). - Re-apply the fix with the new MAC.
- Monitor IPv6 separately from IPv4 (e.g. an external IPv6 ping or HTTP check) so a MAC change doesn't go unnoticed.
- The fix only changes how your server reaches its IPv6 gateway. It opens no ports and has no effect on IPv4.
- If IPv6 still fails after applying this, open a support ticket and include the output of:
ip -6 addr; ip -6 route; ip -6 neigh; nstat -az | grep -i icmp6