OpenWrt fails to obtain IPv6 address on mobile network

Hi, I'm having this issue with OpenWrt.

My setup is a ZTE MF286R with a built-in modem. The modem talks to OpenWrt through NCM.

Now, if I use the old IPv4 APN of the ISP (and IP protocol set to IPv4), it connects and works fine:


However, if I set it to the IPv6-only APN, it fails to obtain a prefix:


As you see, I've left it for a good while, but still no prefix. The usb0 interface for the modem only has a link-local address.

The firewall is set to accept all.

The logs for odhcp6c:

daemon.err odhcp6c[24151]: Failed to send RS (Address not available)
daemon.err odhcp6c[24151]: Failed to send SOLICIT message to ff02::1:2 (Address not available)

And for the mobile interface:

daemon.notice netifd: Interface 'lte' is setting up now
daemon.notice netifd: lte (23752): sending -> AT+CFUN=1
daemon.notice netifd: lte (23752): Configuring modem
daemon.notice netifd: lte (23752): sending -> AT+ZGDCONT=1,"IPV6","internetipv6","",0,0
daemon.notice netifd: lte (23752): sending -> AT+ZGPCOAUTH=1,"internet","internet",0
daemon.notice netifd: lte (23752): Starting network lte
daemon.notice netifd: lte (23752): Connecting modem
daemon.notice netifd: lte (23752): sending -> AT+ZGACT=1,1
daemon.notice netifd: lte (23752): Setting up usb0
daemon.notice netifd: Interface 'lte' is now up
daemon.notice netifd: Interface 'lte_6' is enabled
daemon.notice netifd: Interface 'lte_6' has link connectivity
daemon.notice netifd: Interface 'lte_6' is setting up now
user.notice firewall: Reloading firewall due to ifup of lte (usb0)

If I run odhcp6c usb0 manually, it just hangs forever. The APN settings are definitely correct, they work on another device and my ISP supports IPv6.

Sending AT+CGCONTRDP to the modem only returns a link-local address and the IPv6 DNS of my ISP. No GUA prefix. This particular ISP assigns a standard /64 prefix - it works on an Android device, including the hotspot with a /64.

One thing that's odd is that setting the "IP Protocol" to "IPv4+IPv6" (dual-stack) on the IPv4-only APN also fails to obtain an IPv4 address or IPv6 prefix (!):

I would appreciate any help here on how to get the IPv6 APN working.

Please show the output:

AT+CGDCONT?
AT+CGPADDR
AT+CGCONTRDP

Please consider changing "prefix" to "address" in the topic title to avoid confusion with prefix delegation.

1 Like

Here are the responses from the modem on /dev/ttyACM0:

AT+CGDCONT?
+CGDCONT: 1,"IPV6","internetipv6.mnc003.mcc260.gprs","254.128.0.0.0.0.0.0.0.0.0.37.88.136.248.1",0,0,,,,
AT+CGPADDR
+CGPADDR: 1, "254.128.0.0.0.0.0.0.0.0.0.37.88.136.248.1"
AT+CGCONRDP
ERROR
Also the AT+CGCONTRDP I was talking about (CONT instead of CON):
+CGCONTRDP: 1,5,"internetipv6.mnc003.mcc260.gprs","254.128.0.0.0.0.0.0.0.0.0.37.88.136.248.1","","42.1.23.0.0.2.255.255.0.0.0.0.0.0.159.1","42.1.23.0.0.3.255.255.0.0.0.0.0.0.152.34","","",0,0
I've just noticed the 254.128 is a link-local address, but NOT the one on the usb0 interface (in a strange format - 254=fe and 128=80) and the 42.1.23 addresses are the IPv6 DNS of my ISP. Unfortunately, there's no GUA/public address for the router.

Apologies for the typo.
Run AT+CGPIAF=1,1,1,0 once and then repeat the commands above.

1 Like
AT+CGPIAF=1,1,1,0
OK
AT+CGDCONT?
+CGDCONT: 1,"IPV6","internetipv6.mnc003.mcc260.gprs","FE80:0000:0000:0000:0000:0025:5888:F801",0,0,,,,

OK
AT+CGPADDR
+CGPADDR: 1, "FE80:0000:0000:0000:0000:0025:5888:F801"

OK
AT+CGCONTRDP
+CGCONTRDP: 1,5,"internetipv6.mnc003.mcc260.gprs","FE80:0000:0000:0000:0000:0025:5888:F801","","2A01:1700:0002:FFFF:0000:0000:0000:9F01","2A01:1700:0003:FFFF:0000:0000:0000:9822","","",0,0

OK

I'm using picocom and the modem doesn't send the individual characters (of the commands) back, but I copied the commands into the blank lines to make it easier to read. Normally the serial console looks like this:


OK

+CGDCONT: 1,"IPV6","internetipv6.mnc003.mcc260.gprs","FE80:0000:0000:0000:0000:0025:5888:F801",0,0,,,,

OK

+CGPADDR: 1, "FE80:0000:0000:0000:0000:0025:5888:F801"

OK

+CGCONTRDP: 1,5,"internetipv6.mnc003.mcc260.gprs","FE80:0000:0000:0000:0000:0025:5888:F801","","2A01:1700:0002:FFFF:0000:0000:0000:9F01","2A01:1700:0003:FFFF:0000:0000:0000:9822","","",0,0

OK

It appears that I've made it work... manually. What I've done is

ip -6 addr add fe80::0025:5888:f801/64 dev usb0

which assigns that LL address from the AT+CGCONTRDP to the usb0 interface, and then just:

odhcp6c -v usb0

which gets me the GUA address and then I'm happy to say everything works perfectly. Thanks for the guidance, to be honest I wouldn't have thought about assigning that address to usb0 if it wasn't in the proper format.

The only question is, why doesn't this work automatically through NCM?

1 Like

Good job! I guess the connection script is not capable to handle all the possible situations. Cannot check right now.

1 Like

Assuming you're now in IPv6 only mode could you please run a few commands on the router for me:

nslookup openwrt.de.
nslookup ipv4only.arpa.
root@OpenWrt:~# nslookup openwrt.de.
Server:         127.0.0.1
Address:        127.0.0.1:53

Non-authoritative answer:
Name:   openwrt.de
Address: 64.190.63.222

Non-authoritative answer:

root@OpenWrt:~# nslookup ipv4only.arpa.
Server:         127.0.0.1
Address:        127.0.0.1:53

Non-authoritative answer:
Name:   ipv4only.arpa
Address: 192.0.0.170
Name:   ipv4only.arpa
Address: 192.0.0.171

Non-authoritative answer:
Name:   ipv4only.arpa
Address: 64:ff9b::c000:ab

I believe my ISP relies solely on CLAT for IPv4 connectivity, but that also comes up automatically as I have the 464xlat package installed, so everything works.

1 Like

Thank you! That's interesting - they do support Pref64 discovery - DNS properly responds with the default prefix 64:ff9b::.
At the same time there are no signs of DNS64, e.g. no synthetic AAAA response back when you ask the DNS for IPv4 only hostname.
See if you can ping 64:ff9b::40be:3f6f - this is a synthetic address for IPv4 only hostname openwrt.de, this is what DNS64 capable server should return to you.

I can confirm that a ping to 64:ff9b::40be:3f6f does work:

root@OpenWrt:~# ping -c4 64:ff9b::40be:3f6f
PING 64:ff9b::40be:3f6f (64:ff9b::40be:3f6f): 56 data bytes
64 bytes from 64:ff9b::40be:3f6f: seq=0 ttl=52 time=43.279 ms
64 bytes from 64:ff9b::40be:3f6f: seq=1 ttl=52 time=38.825 ms
64 bytes from 64:ff9b::40be:3f6f: seq=2 ttl=52 time=40.665 ms
64 bytes from 64:ff9b::40be:3f6f: seq=3 ttl=52 time=38.270 ms

--- 64:ff9b::40be:3f6f ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 38.270/40.259/43.279 ms

For anyone reading this I also have a script for automatically extracting the LL address, assigning it to the interface and running odhcp6c:

MODEM_DEVICE="/dev/ttyACM0"

exec 3<> "$MODEM_DEVICE"
echo -e "AT+CGPIAF=1,1,1,0\r" >&3
echo -e "AT+CGCONTRDP\r" >&3
sleep 2 # Give some time for the modem to respond

RESPONSE=""
while IFS= read -r line; do
    RESPONSE="${RESPONSE}${line}"
    # Check if the line contains the expected response prefix using case statement
    case "$line" in
        *+CGCONTRDP*) break ;;
    esac
done <&3

exec 3>&-

IPV6_ADDRESS=$(echo "$RESPONSE" | grep -o '+CGCONTRDP:[^,]*,[^,]*,[^,]*,"[^"]*"' | cut -d'"' -f4)

if [ -n "$IPV6_ADDRESS" ]; then
    ip -6 addr add "$IPV6_ADDRESS"/64 dev usb0
    echo "IPv6 address $IPV6_ADDRESS added to usb0."
else
    echo "Failed to extract IPv6 address from the modem response."
fi

echo "Running odhcp6c for 10 seconds..."
timeout 10 odhcp6c -v usb0
echo "Done"

Note that it has no error-handling and is partially ChatGPT-written, so exercise caution if using it. But after saving it to /root/fix_dhcpv6.sh, doing chmod +x /root/fix_dhcpv6.sh and then adding

sleep 30
ash /root/fix_dhcpv6.sh

to Local Startup everything works perfectly after a reboot.

Thanks again. You can manually assign DNS64 servers (Google, Cloudflare, etc) to the interface to avoid address translation performed on your side.

If you need to extract the address only, then you only need to parse +CGPADDR output.

You can avoid using echo as shown here: Get result of AT command to a variable in Bash script - #4 by AndrewZ

Instead of running odhcp6c you can create or update a virtual interface, you can see examples in various OpenWrt connection scripts like qmi.sh etc.