Flint 3 - No IPv6 on LAN but working on WAN after rebooting ISP ONT

Hello!

I recently bought a Flint 3, and it has been amazing so far. This is my first GL.iNet router, and I’m really enjoying it.

I am having one issue with IPv6. I am using Native mode, and it works fine in general, but it fails in one specific case:

If I restart the ISP ONT(bridge mode) where the fiber connection terminates, the Flint 3 itself still has working IPv6. I can ping from the Flint, services like AdGuard Home can resolve IPv6 addresses, and nslookup from LAN clients returns IPv6 records as well.

However, LAN devices themselves do not have working IPv6 connectivity until I restart the Flint 3. I created a small hotplug script that restarts wan6, and that fixes the issue, but I’m wondering if there is a cleaner or more permanent fix that does not rely on a hotplug script.

This IPv6 LAN issue does not happen when I restart the Flint 3. It only happens after rebooting the ISP ONT/router.

#!/bin/sh

[ "$ACTION" = ifup -o "$ACTION" = ifdown -o "$ACTION" = ifupdate ] || exit 0
[ "$ACTION" = ifupdate -a -z "$IFUPDATE_ADDRESSES" -a -z "$IFUPDATE_DATA" -a -z "$IFUPDATE_PREFIXES" ] && exit 0

/etc/init.d/firewall enabled || exit 0

fw3 -q network "$INTERFACE" >/dev/null

logger -t firewall "Reloading firewall due to $ACTION of $INTERFACE ($DEVICE)"

if [ "$INTERFACE" = "wan6" ]; then
    (
        sleep 1
        fw3 -q reload

        if [ "$ACTION" = "ifup" ] && [ -e /tmp/wan6-renew-recovery-in-progress ] && [ ! -e /tmp/wan6-needs-renew-after-linkloss ]; then
            rm -f /tmp/wan6-renew-recovery-in-progress
            rm -f /tmp/wan6-needs-second-renew-after-prefix-update
            rm -f /tmp/wan6-restart-odhcpd-after-second-renew

            logger -t wan6-renew-after-firewall "wan6 final ifup after recovery; restarting odhcpd for LAN RA"
            ubus call service restart '{"name":"odhcpd"}'
        fi

        if [ "$ACTION" = "ifdown" ] && [ -e /tmp/wan6-renew-recovery-in-progress ]; then
            rm -f /tmp/wan6-needs-second-renew-after-prefix-update
            rm -f /tmp/wan6-restart-odhcpd-after-second-renew
            touch /tmp/wan6-needs-renew-after-linkloss

            logger -t wan6-renew-after-firewall "wan6 went down during renew recovery; re-arming first renew"
        fi

        if [ "$ACTION" = "ifup" ] && [ -e /tmp/wan6-needs-renew-after-linkloss ]; then
            rm -f /tmp/wan6-needs-renew-after-linkloss
            rm -f /tmp/wan6-needs-second-renew-after-prefix-update
            rm -f /tmp/wan6-restart-odhcpd-after-second-renew
            touch /tmp/wan6-renew-recovery-in-progress
            touch /tmp/wan6-needs-second-renew-after-prefix-update

            logger -t wan6-renew-after-firewall "wan6 ifup after link loss; firewall reload done; forcing first netifd renew"
            ubus call network.interface.wan6 renew
        fi

        if [ "$ACTION" = "ifupdate" ] && [ "$IFUPDATE_PREFIXES" = "1" ] && [ -e /tmp/wan6-restart-odhcpd-after-second-renew ]; then
            if ifstatus wan6 2>/dev/null | grep -q '"ipv6-address"' && ifstatus wan6 2>/dev/null | grep -q '"ipv6-prefix"'; then
                rm -f /tmp/wan6-restart-odhcpd-after-second-renew
                rm -f /tmp/wan6-renew-recovery-in-progress

                logger -t wan6-renew-after-firewall "wan6 ifupdate after second renew; restarting odhcpd for LAN RA"
                ubus call service restart '{"name":"odhcpd"}'
            fi
        fi

        if [ "$ACTION" = "ifupdate" ] && [ "$IFUPDATE_PREFIXES" = "1" ] && [ -e /tmp/wan6-needs-second-renew-after-prefix-update ]; then
            if ifstatus wan6 2>/dev/null | grep -q '"ipv6-address"' && ifstatus wan6 2>/dev/null | grep -q '"ipv6-prefix"'; then
                rm -f /tmp/wan6-needs-second-renew-after-prefix-update
                touch /tmp/wan6-restart-odhcpd-after-second-renew

                logger -t wan6-renew-after-firewall "wan6 valid address and prefix after first renew; forcing second netifd renew"
                ubus call network.interface.wan6 renew
            else
                logger -t wan6-renew-after-firewall "wan6 prefix update seen but IPv6 data not ready; waiting for next ifupdate"
            fi
        fi
    ) &
else
    fw3 -q reload
fi

Final version here.

I think I found a cleaner workaroundfor the IPv6 LAN issue after an ONT restart:

After the ONT restarted, wan6 looked fine from OpenWrt:

ifstatus wan6

showed up: true, a valid IPv6 address, a delegated prefix, and LAN also received the delegated prefix correctly. However, IPv6 traffic from LAN clients to the internet did not work.

The strange part was that IPv6 from the router itself worked, but traffic sourced from the LAN delegated prefix did not. For example:

ping -6 -I eth0 2606:4700:4700::1111

worked, but:

ping -6 -I <br-lan IPv6 address> 2606:4700:4700::1111

failed after the ONT came back online.

A manual restart of wan6 fixed it:

ifdown wan6
ifup wan6

After testing, I found that the real fix is not to restart wan6, but to force DHCPv6-PD renewal through netifd after the interface and firewall/kmwan state have settled.

The command that fixes the issue manually is:

ubus call network.interface.wan6 renew

However, one renew immediately after ifup wan6 was not enough in my tests. The first renew triggers an ifupdate event with:

IFUPDATE_PREFIXES=1

Then a second renew after that prefix update fixes LAN IPv6 reliably.

So the working sequence is:

ONT goes down
→ wan6 teardown is detected
→ mark that wan6 needs DHCPv6-PD recovery

ONT comes back
→ wan6 ifup
→ kmwan adds wan6
→ firewall reloads for wan6 ifup
→ first netifd renew

first renew causes:
→ ifupdate wan6 with IFUPDATE_PREFIXES=1
→ firewall reloads again
→ second netifd renew

LAN IPv6 works again

The important detail is that /etc/hotplug.d/iface/20-firewall originally ignores ifupdate events that only contain prefix changes, because it only checks IFUPDATE_ADDRESSES and IFUPDATE_DATA. In this case the important event is IFUPDATE_PREFIXES=1, so that condition needs to include IFUPDATE_PREFIXES.

The changes I made were:

In /lib/netifd/proto/dhcpv6.sh, mark a real wan6 teardown:

proto_dhcpv6_teardown() {
        local interface="$1"

        if [ "$interface" = "wan6" ]; then
                touch /tmp/wan6-needs-renew-after-linkloss
                logger -t dhcpv6.sh "wan6 teardown marked; will force DHCPv6 renew after firewall and prefix update"
        fi

        proto_kill_command "$interface"
}

Then in /etc/hotplug.d/iface/20-firewall, allow prefix-only updates and run two staged renews:

#!/bin/sh

[ "$ACTION" = ifup -o "$ACTION" = ifdown -o "$ACTION" = ifupdate ] || exit 0
[ "$ACTION" = ifupdate -a -z "$IFUPDATE_ADDRESSES" -a -z "$IFUPDATE_DATA" -a -z "$IFUPDATE_PREFIXES" ] && exit 0

/etc/init.d/firewall enabled || exit 0

fw3 -q network "$INTERFACE" >/dev/null

logger -t firewall "Reloading firewall due to $ACTION of $INTERFACE ($DEVICE)"

if [ "$INTERFACE" = "wan6" ]; then
    (
        sleep 1
        fw3 -q reload

        if [ "$ACTION" = "ifup" ] && [ -e /tmp/wan6-renew-recovery-in-progress ] && [ ! -e /tmp/wan6-needs-renew-after-linkloss ]; then
            rm -f /tmp/wan6-renew-recovery-in-progress
            rm -f /tmp/wan6-needs-second-renew-after-prefix-update
            rm -f /tmp/wan6-restart-odhcpd-after-second-renew

            logger -t wan6-renew-after-firewall "wan6 final ifup after recovery; restarting odhcpd for LAN RA"
            ubus call service restart '{"name":"odhcpd"}'
        fi

        if [ "$ACTION" = "ifdown" ] && [ -e /tmp/wan6-renew-recovery-in-progress ]; then
            rm -f /tmp/wan6-needs-second-renew-after-prefix-update
            rm -f /tmp/wan6-restart-odhcpd-after-second-renew
            touch /tmp/wan6-needs-renew-after-linkloss

            logger -t wan6-renew-after-firewall "wan6 went down during renew recovery; re-arming first renew"
        fi

        if [ "$ACTION" = "ifup" ] && [ -e /tmp/wan6-needs-renew-after-linkloss ]; then
            rm -f /tmp/wan6-needs-renew-after-linkloss
            rm -f /tmp/wan6-needs-second-renew-after-prefix-update
            rm -f /tmp/wan6-restart-odhcpd-after-second-renew
            touch /tmp/wan6-renew-recovery-in-progress
            touch /tmp/wan6-needs-second-renew-after-prefix-update

            logger -t wan6-renew-after-firewall "wan6 ifup after link loss; firewall reload done; forcing first netifd renew"
            ubus call network.interface.wan6 renew
        fi

        if [ "$ACTION" = "ifupdate" ] && [ "$IFUPDATE_PREFIXES" = "1" ] && [ -e /tmp/wan6-restart-odhcpd-after-second-renew ]; then
            if ifstatus wan6 2>/dev/null | grep -q '"ipv6-address"' && ifstatus wan6 2>/dev/null | grep -q '"ipv6-prefix"'; then
                rm -f /tmp/wan6-restart-odhcpd-after-second-renew
                rm -f /tmp/wan6-renew-recovery-in-progress

                logger -t wan6-renew-after-firewall "wan6 ifupdate after second renew; restarting odhcpd for LAN RA"
                ubus call service restart '{"name":"odhcpd"}'
            fi
        fi

        if [ "$ACTION" = "ifupdate" ] && [ "$IFUPDATE_PREFIXES" = "1" ] && [ -e /tmp/wan6-needs-second-renew-after-prefix-update ]; then
            if ifstatus wan6 2>/dev/null | grep -q '"ipv6-address"' && ifstatus wan6 2>/dev/null | grep -q '"ipv6-prefix"'; then
                rm -f /tmp/wan6-needs-second-renew-after-prefix-update
                touch /tmp/wan6-restart-odhcpd-after-second-renew

                logger -t wan6-renew-after-firewall "wan6 valid address and prefix after first renew; forcing second netifd renew"
                ubus call network.interface.wan6 renew
            else
                logger -t wan6-renew-after-firewall "wan6 prefix update seen but IPv6 data not ready; waiting for next ifupdate"
            fi
        fi
    ) &
else
    fw3 -q reload
fi

I think the underlying bug is that after an ONT/link recovery, DHCPv6-PD appears valid locally, but the delegated prefix path is not fully restored for LAN traffic until netifd performs additional DHCPv6 renew handling after the prefix update event.

GL-BE9300 is running 4.9.0.

What I tried:

A simple odhcpd restart is not enough. A single:

ubus call network.interface.wan6 renew

also does not always fix it.

The reliable recovery sequence I found is:

1. wan6 comes back up after link loss
2. run: ubus call network.interface.wan6 renew
3. wait for ifupdate on wan6 with IFUPDATE_PREFIXES=1
4. confirm wan6 has both ipv6-address and ipv6-prefix
5. run a second: ubus call network.interface.wan6 renew
6. after the second ifupdate, restart odhcpd so LAN RA/DHCPv6 is refreshed

So it looks like after ONT/link recovery there is a race or stale state between odhcp6c, netifd, kmwan, firewall reload, and odhcpd. wan6 appears valid too early, but the delegated prefix is not fully usable for LAN traffic until a second DHCPv6 renew after the prefix update event.

It would be good if GL.iNet could fix this in firmware so that after wan6 link recovery, DHCPv6-PD and LAN RA are properly refreshed without requiring manual workarounds.

Hi

This seems a bit unusual.

The QSDK version of /etc/hotplug.d/iface/20-firewall does not appear to be significantly different from the one in vanilla OpenWrt:


Based on your description, is the issue you're experiencing the following?

  • After the ONT reboots, the DHCPv6 PD provided by the upstream ISP changes, and the old prefix is no longer valid.
  • However, the br-lan/LAN devices continue using the old delegated prefix, causing IPv6 connectivity to stop working.
  • You then need to either run ifdown wan6 && ifup wan6 or execute ubus call network.interface.wan6 renew twice to restore IPv6 connectivity.

We tested this locally using a Flint 3 running v4.9.0 in a simulated environment, but we were unable to reproduce the issue:

  1. Before the simulated ONT reboot:

  2. During the simulated ONT reboot:


    (The affected addresses are marked as Deprecated.)

  3. After the simulated ONT reboot, with a changed DHCPv6 PD:


    (The old addresses are marked as Deprecated, while the new addresses become available.)

Hi,

Yes, that is close, but with one important difference: in my case it does not look like the LAN clients simply keep using an old delegated prefix.

After the ONT comes back online, wan6 appears valid from OpenWrt’s point of view:

  • wan6 is up

  • the router itself has IPv6 connectivity

  • ifstatus wan6 shows an IPv6 address and an IPv6 delegated prefix

  • br-lan also has the delegated prefix

  • LAN clients receive IPv6 addresses from that prefix

However, IPv6 traffic sourced from the LAN prefix does not work.

For example, after the ONT restart, this works from the router:

ping -6 -I eth0 2606:4700:4700::1111

but this fails:

ping -6 -I <br-lan IPv6 address> 2606:4700:4700::1111

Then, if I run:

ubus call network.interface.wan6 renew

it triggers an ifupdate on wan6 with IFUPDATE_PREFIXES=1, but this alone is not always enough. What reliably restores LAN IPv6 is:

1. wan6 comes back up after ONT/link loss
2. run ubus call network.interface.wan6 renew
3. wait for ifupdate on wan6 with IFUPDATE_PREFIXES=1
4. verify that ifstatus wan6 contains both ipv6-address and ipv6-prefix
5. run ubus call network.interface.wan6 renew again
6. wait for the second ifupdate
7. restart odhcpd so LAN RA/DHCPv6 is refreshed

Also, to clarify why I used /etc/hotplug.d/iface/20-firewall for the workaround: I do not think the firewall itself is the root cause.

I used that file because it is a convenient hotplug point that runs after wan6 interface events, and it sees both:

ifup wan6
ifupdate wan6 with IFUPDATE_PREFIXES=1

Those are exactly the events needed to reproduce the recovery sequence.

Also let me give you some context regarding my IPv6 configuration:

The ONT receives two /56 IPv6 prefixes: one prefix is used by the ONT itself, and the other is the prefix I want to use directly on my Flint 3.

To avoid using anything delegated or advertised by the ONT, I added two firewall rules in LuCI to block:

ICMPv6 Router Advertisement from the ONT
DHCPv6-PD from the ONT

This way, my router does not use the ONT-provided IPv6 information. It uses the prefix coming directly from the Orange DHCPv6 server instead.

Update on the situation: Tonight I realized my ipv6 stopped working again on lan even without an ONT restart and I ran a series of commands to show you the situation. Hope it helps.

I ran these commands after i realised ipv6 was not working on lan.

In the broken state, the router itself still has working IPv6 connectivity on WAN:

root@GL-BE9300:~# ping -6 -c 3 -I eth0 2606:4700:4700::1111
PING 2606:4700:4700::1111 (2606:4700:4700::1111): 56 data bytes
64 bytes from 2606:4700:4700::1111: seq=0 ttl=60 time=16.182 ms
64 bytes from 2606:4700:4700::1111: seq=1 ttl=60 time=16.007 ms
64 bytes from 2606:4700:4700::1111: seq=2 ttl=60 time=15.740 ms

--- 2606:4700:4700::1111 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss

A normal IPv6 ping from the router also works:

root@GL-BE9300:~# ping -6 -c 3 2606:4700:4700::1111
PING 2606:4700:4700::1111 (2606:4700:4700::1111): 56 data bytes
64 bytes from 2606:4700:4700::1111: seq=0 ttl=60 time=16.091 ms
64 bytes from 2606:4700:4700::1111: seq=1 ttl=60 time=15.602 ms
64 bytes from 2606:4700:4700::1111: seq=2 ttl=60 time=15.960 ms

--- 2606:4700:4700::1111 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss

wan6 also looks valid. It is up, has an IPv6 address, and has a delegated prefix:

{
        "up": true,
        "available": true,
        "l3_device": "eth0",
        "proto": "dhcpv6",
        "device": "eth0",
        "delegation": true,
        "ipv6-address": [
                {
                        "address": "2a02:a58:8906:xxxx::1",
                        "mask": 128,
                        "preferred": 44285,
                        "valid": 47885
                }
        ],
        "ipv6-prefix": [
                {
                        "address": "2a02:a58:96be:xx00::",
                        "mask": 56,
                        "preferred": 44285,
                        "valid": 47885,
                        "class": "wan6",
                        "assigned": {
                                "lan": {
                                        "address": "2a02:a58:96be:xx00::",
                                        "mask": 60
                                }
                        }
                }
        ],
        "route": [
                {
                        "target": "::",
                        "mask": 0,
                        "nexthop": "fe80::2621:24ff:feXX:XXXX",
                        "metric": 512,
                        "source": "2a02:a58:96be:xx00::/56"
                },
                {
                        "target": "::",
                        "mask": 0,
                        "nexthop": "fe80::2621:24ff:feXX:XXXX",
                        "metric": 512,
                        "source": "2a02:a58:8906:xxxx::1/128"
                }
        ],
        "dns-server": [
                "2a02:a58:21:21::1",
                "2a02:a58:21:21::2"
        ]
}

br-lan also has an address from the delegated public prefix:

root@GL-BE9300:~# ip -6 addr show br-lan
16: br-lan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    inet6 2a02:a58:96be:xx00::1/60 scope global dynamic noprefixroute
       valid_lft 47875sec preferred_lft 44275sec
    inet6 fe80::9683:c4ff:feXX:XXXX/64 scope link
       valid_lft forever preferred_lft forever

However, traffic sourced from the LAN delegated prefix does not work:

root@GL-BE9300:~# ping -6 -c 3 -I 2a02:a58:96be:xx00::1 2606:4700:4700::1111
PING 2606:4700:4700::1111 (2606:4700:4700::1111) from 2a02:a58:96be:xx00::1: 56 data bytes

--- 2606:4700:4700::1111 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

The IPv6 routing table also looks correct. There is a default route for the delegated prefix via the upstream link-local gateway:

root@GL-BE9300:~# ip -6 route show
default from 2a02:a58:8906:xxxx::1 via fe80::2621:24ff:feXX:XXXX dev eth0 proto static metric 512 pref medium
default from 2a02:a58:96be:xx00::/56 via fe80::2621:24ff:feXX:XXXX dev eth0 proto static metric 512 pref medium
2a02:a58:96be:xx00::/64 dev br-lan proto static metric 1024 pref medium
unreachable 2a02:a58:96be:xx00::/56 dev lo proto static metric 2147483647 pref medium
default via fe80::2621:24ff:feXX:XXXX dev eth0 proto static metric 10 pref medium
default via fe80::2621:24ff:feXX:XXXX dev eth0 proto ra metric 1024 expires 3984sec hoplimit 64 pref medium

Even route lookup for traffic sourced from the LAN IPv6 address selects the correct WAN path:

root@GL-BE9300:~# ip -6 route get 2606:4700:4700::1111 from 2a02:a58:96be:xx00::1 iif br-lan
2606:4700:4700::1111 from 2a02:a58:96be:xx00::1 via fe80::2621:24ff:feXX:XXXX dev eth0 proto static metric 512 iif br-lan pref medium

So the visible configuration looks correct:

  • router WAN IPv6 works

  • wan6 is up

  • wan6 has an IPv6 address

  • wan6 has a delegated prefix

  • br-lan has an IPv6 address from the delegated prefix

  • route lookup for traffic from the LAN prefix selects the correct upstream route

But traffic sourced from the LAN delegated prefix still fails.

Then I ran a manual renew:

root@GL-BE9300:~# ubus call network.interface.wan6 renew

Immediately after that, the same ping sourced from the LAN IPv6 address started working:

root@GL-BE9300:~# ping -6 -c 3 -I 2a02:a58:96be:xx00::1 2606:4700:4700::1111
PING 2606:4700:4700::1111 (2606:4700:4700::1111) from 2a02:a58:96be:xx00::1: 56 data bytes
64 bytes from 2606:4700:4700::1111: seq=0 ttl=60 time=15.806 ms
64 bytes from 2606:4700:4700::1111: seq=1 ttl=60 time=16.006 ms
64 bytes from 2606:4700:4700::1111: seq=2 ttl=60 time=16.060 ms

--- 2606:4700:4700::1111 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss

This suggests that the problem is not simply missing IPv6 configuration on LAN. The IPv6 address, delegated prefix, and routes are present and look correct, but the delegated prefix is not actually usable for LAN-sourced traffic until wan6 is renewed again.

The router appears to have the correct IPv6 information, but traffic from the delegated LAN prefix does not work until ubus call network.interface.wan6 renew is executed.

Small update after more testing:

I narrowed the issue down further. While LAN IPv6 was broken but WAN IPv6 was still working, I tested several recovery actions:

fw3 reload only       -> did not fix it
kmwan restart only    -> did not fix it
kill -USR1 odhcp6c    -> fixed it immediately
ubus wan6 renew       -> also works, but odhcp6c USR1 had already restored it

The key finding is that sending SIGUSR1 directly to the odhcp6c process restores LAN IPv6 immediately:

kill -USR1 $(pidof odhcp6c)

After this, traffic sourced from the br-lan IPv6 address starts working again.

So this now looks like an odhcp6c / DHCPv6-PD renew-state issue. The delegated prefix is visible in netifd and assigned to LAN, but traffic from that prefix is not usable until odhcp6c performs a renew.

I also tested a much simpler workaround: after wan6 ifup, after the normal firewall reload, send SIGUSR1 to the odhcp6c process for eth0.

Simplified sequence:

wan6 ifup
→ firewall reload
→ SIGUSR1 to odhcp6c

This restored LAN IPv6 after ONT restart in my tests, without needing the previous more complex two-renew sequence.

I captured the broken state with tcpdump on eth0.

When LAN IPv6 was broken, packets sourced from the delegated LAN prefix were actually leaving the router correctly:

2a02:a58:96be:xx00::1 > 2606:4700:4700::1111 ICMPv6 echo request

They were sent to the ISP gateway MAC

But no echo replies came back.

After forcing a renew on odhcp6c, the exact same test immediately started receiving replies:

2606:4700:4700::1111 > 2a02:a58:96be:xx00::1 ICMPv6 echo reply

So the router is sending LAN-prefix traffic out correctly, but the upstream return path for the delegated prefix appears inactive/stale until odhcp6c renews the DHCPv6-PD state.

I created a case with my ISP to see if they have something missconfigured. Will keep you updated.