IPv6 NAT6 on clients does not work until I restart odhcpd

I'm not sure if I have found a bug, but I can reproduce this issue very easily on each reboot of my router. My LAN clients are unable to communicate with the internet on IPv6 upon booting, if I SSH into the router and run /etc/init.d/odhcpd restart then it will begin working.

  1. Any idea what may be going on?
    2/ Any workarounds I can do to make this automatic if I must keep doing this manual command often?
  2. OpenWrt errors out the first time I run the command - claims "Not found" yet it fixes the issue too.
 -----------------------------------------------------
 OpenWrt 19.07.7, r11306-c4a6851c72
 -----------------------------------------------------
root@meow:~# /etc/init.d/odhcpd restart
Command failed: Not found
root@meow:~# /etc/init.d/odhcpd restart
root@meow:~# ls -lah /etc/init.d/odhcpd
-rwxr-xr-x    1 root     root         292 Feb 15 10:22 /etc/init.d/odhcpd
root@meow:~# ls -lah /etc/init.d/odhcpd
-rwxr-xr-x    1 root     root         292 Feb 15 10:22 /etc/init.d/odhcpd
root@meow:~# cat /etc/init.d/odhcpd
#!/bin/sh /etc/rc.common

START=35
STOP=85
USE_PROCD=1

start_service() {
        procd_open_instance
        procd_set_param command /usr/sbin/odhcpd
        procd_set_param respawn
        procd_close_instance
}

reload_service() {
        procd_send_signal odhcpd
}

service_triggers()
{
        procd_add_reload_trigger "dhcp"
}

config export

root@meow:~# uci export
package dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option filterwin2k '0'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option expandhosts '1'
        option nonegcache '0'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.auto'
        option nonwildcard '1'
        option localservice '1'
        option domain 'gfm'
        option local '/gfm/'

config dhcp 'lan'
        option interface 'lan'
        option leasetime '12h'
        option ra 'server'
        option ra_management '1'
        option ra_default '1'
        option start '20'
        option dhcpv6 'server'
        option limit '50'

config dhcp 'wan'
        option interface 'wan'
        option ignore '1'

config odhcpd 'odhcpd'
        option maindhcp '0'
        option leasefile '/tmp/hosts/odhcpd'
        option leasetrigger '/usr/sbin/odhcpd-update'
        option loglevel '4'

package dropbear

config dropbear
        option PasswordAuth 'on'
        option RootPasswordAuth 'on'
        option Port '22'

package firewall

config defaults
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'

config zone
        option name 'lan'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'ACCEPT'
        option network 'lan'

config zone
        option name 'wan'
        list network 'wan'
        list network 'wan6'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option input 'ACCEPT'
        option masq6 '1'

config forwarding
        option src 'lan'
        option dest 'wan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option src_ip 'fc00::/6'
        option dest_ip 'fc00::/6'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option enabled '0'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config include
        option path '/etc/firewall.user'

config include
        option path '/etc/firewall.nat6'
        option name 'nat6'
        option reload '1'

config zone
        option device 'eth0.666'
        option forward 'ACCEPT'
        option input 'REJECT'
        option name 'untrusted'
        option output 'ACCEPT'

package luci

config core 'main'
        option lang 'auto'
        option mediaurlbase '/luci-static/bootstrap'
        option resourcebase '/luci-static/resources'
        option ubuspath '/ubus/'

config extern 'flash_keep'
        option uci '/etc/config/'
        option dropbear '/etc/dropbear/'
        option openvpn '/etc/openvpn/'
        option passwd '/etc/passwd'
        option opkg '/etc/opkg.conf'
        option firewall '/etc/firewall.user'
        option uploads '/lib/uci/upload/'

config internal 'languages'

config internal 'sauth'
        option sessionpath '/tmp/luci-sessions'
        option sessiontime '3600'

config internal 'ccache'
        option enable '1'

config internal 'themes'
        option Bootstrap '/luci-static/bootstrap'

config internal 'apply'
        option rollback '90'
        option holdoff '4'
        option timeout '5'
        option display '1.5'

config internal 'diag'
        option dns 'openwrt.org'
        option ping 'openwrt.org'
        option route 'openwrt.org'

package network

config interface 'loopback'
        option ifname 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option ula_prefix 'fdb6:bf3f:ddd8::/48'

config interface 'lan'
        option type 'bridge'
        option ifname 'eth0'
        option proto 'static'
        option netmask '255.255.255.0'
        option ip6assign '60'
        option ipaddr '192.168.44.1'
        option ipv6 'on'

config interface 'wan'
        option ifname 'eth1'
        option proto 'dhcp'

config interface 'wan6'
        option ifname 'eth1'
        option proto 'dhcpv6'
        option reqaddress 'try'
        option reqprefix 'no'
        option defaultroute '1'

config interface 'vlan666'
        option ifname 'eth0.666'
        option ipaddr '172.17.0.1'
        option netmask '255.255.255.0'
        option proto 'static'

package rpcd

config rpcd
        option socket '/var/run/ubus.sock'
        option timeout '30'

config login
        option username 'root'
        option password '$p$root'
        list read '*'
        list write '*'

package system

config system
        option ttylogin '0'
        option log_size '64'
        option urandom_seed '0'
        option hostname 'meow'
        option timezone 'EST5EDT,M3.2.0,M11.1.0'
        option zonename 'America/New York'

config timeserver 'ntp'
        option enabled '1'
        option enable_server '0'
        list server '0.openwrt.pool.ntp.org'
        list server '1.openwrt.pool.ntp.org'
        list server '2.openwrt.pool.ntp.org'
        list server '3.openwrt.pool.ntp.org'

package ucitrack

config network
        option init 'network'
        list affects 'dhcp'
        list affects 'radvd'

config wireless
        list affects 'network'

config firewall
        option init 'firewall'
        list affects 'luci-splash'
        list affects 'qos'
        list affects 'miniupnpd'

config olsr
        option init 'olsrd'

config dhcp
        option init 'dnsmasq'
        list affects 'odhcpd'

config odhcpd
        option init 'odhcpd'

config dropbear
        option init 'dropbear'

config httpd
        option init 'httpd'

config fstab
        option exec '/sbin/block mount'

config qos
        option init 'qos'

config system
        option init 'led'
        option exec '/etc/init.d/log reload'
        list affects 'luci_statistics'
        list affects 'dhcp'

config luci_splash
        option init 'luci_splash'

config upnpd
        option init 'miniupnpd'

config ntpclient
        option init 'ntpclient'

config samba
        option init 'samba'

config tinyproxy
        option init 'tinyproxy'

package uhttpd

config uhttpd 'main'
        list listen_http '0.0.0.0:80'
        list listen_http '[::]:80'
        list listen_https '0.0.0.0:443'
        list listen_https '[::]:443'
        option redirect_https '1'
        option home '/www'
        option rfc1918_filter '1'
        option max_requests '3'
        option max_connections '100'
        option cert '/etc/uhttpd.crt'
        option key '/etc/uhttpd.key'
        option cgi_prefix '/cgi-bin'
        list lua_prefix '/cgi-bin/luci=/usr/lib/lua/luci/sgi/uhttpd.lua'
        option script_timeout '60'
        option network_timeout '30'
        option http_keepalive '20'
        option tcp_keepalive '1'

config cert 'defaults'
        option days '730'
        option key_type 'rsa'
        option bits '2048'
        option ec_curve 'P-256'
        option country 'ZZ'
        option state 'Somewhere'
        option location 'Unknown'
        option commonname 'OpenWrt'


Maybe there is something interesting in the system logs below; the only thing I could possibly think of is that there may be a conflict with odhcp and dnsmasq-full package but that sorta does not make sense.

root@meow:~# logread -e dhcp6
Sat Jun 26 04:41:48 2021 daemon.err odhcp6c[1838]: Failed to send RS (Address not available)
Sat Jun 26 04:41:49 2021 daemon.err odhcp6c[1838]: Failed to send DHCPV6 message to ff02::1:2 (Address not available)
Sat Jun 26 04:41:50 2021 daemon.err odhcp6c[1838]: Failed to send DHCPV6 message to ff02::1:2 (Address not available)

I found another discussion with some helpful tips to add logging to DHCPv6 up script: Static IPv6 wan6 interface but odhcp6c daemon requests address - #2 by billingd

root@meow:~# logread | grep -i dhcp | grep -v dnsmasq
Sat Jun 26 14:04:37 2021 user.notice ucitrack: Setting up /etc/config/network reload dependency on /etc/config/dhcp
Sat Jun 26 14:04:37 2021 user.notice root: DHCPV6-DEBUG proto_dhcpv6_setup config=wan6
Sat Jun 26 14:04:37 2021 user.notice root: DHCPV6-DEBUG proto_dhcpv6_setup iface=eth1
Sat Jun 26 14:04:37 2021 daemon.notice netifd: wan (1834): udhcpc: started, v1.30.1
Sat Jun 26 14:04:37 2021 user.notice root: DHCPV6-DEBUG proto_dhcpv6_setup reqaddress=try
Sat Jun 26 14:04:37 2021 user.notice root: DHCPV6-DEBUG proto_dhcpv6_setup reqprefix=no
Sat Jun 26 14:04:37 2021 user.notice ucitrack: Setting up /etc/config/dhcp reload dependency on /etc/config/odhcpd
Sat Jun 26 14:04:37 2021 daemon.err odhcp6c[1866]: Failed to send RS (Address not available)
Sat Jun 26 14:04:37 2021 daemon.notice netifd: wan (1834): udhcpc: sending discover
Sat Jun 26 14:04:37 2021 user.notice ucitrack: Setting up /etc/config/system reload dependency on /etc/config/dhcp
Sat Jun 26 14:04:37 2021 daemon.notice netifd: wan (1834): udhcpc: sending select for 192.168.1.113
Sat Jun 26 14:04:37 2021 daemon.notice netifd: wan (1834): udhcpc: lease of 192.168.1.113 obtained, lease time 43200
Sat Jun 26 14:04:38 2021 daemon.err odhcp6c[1866]: Failed to send DHCPV6 message to ff02::1:2 (Address not available)

The issue seems to be daemon.err odhcp6c[1866]: Failed to send DHCPV6 message to ff02::1:2 (Address not available) which occurs during boot time but it succeeds if I run the script a few minutes after booting the router.

Any suggestions?

Since you have a bridge on lan interface, is there any wireless attached to it? If not, disable it. Also use force_link on lan interface, as it solved issues with race conditions in the past.

1 Like

No WIFI on LAN bridge. Let me try with 'force_link' on LAN and re-test.

Also I have a very very dirty hack that doesn't solve the bug but does workaround the problem. Edited the NAT6 script to restart odhcp6c

# NAT6 + masquerading firewall script
# https://github.com/akatrevorjay/openwrt-masq6
# trevorj <github@trevor.joynson.io>
#
# You can configure in /etc/config/firewall per zone:
# * IPv4 masquerading
#     option masq 1
# * IPv6 masquerading
#     option masq6 1
# * IPv6 privacy extensions
#     option masq6_privacy 1
 
set -e -o pipefail
 
. /lib/functions.sh
. /lib/functions/network.sh
. /usr/share/libubox/jshn.sh
 
log() {
    logger -t nat6 -s "${@}"
}
 
get_ula_prefix() {
    uci get network.globals.ula_prefix
}
 
validate_ula_prefix() {
    local ula_prefix="${1}"
    if [ $(echo "${ula_prefix}" | grep -c -E -e "^([0-9a-fA-F]{4}):([0-9a-fA-F]{0,4}):") -ne 1 ] ; then
        log "Fatal error: IPv6 ULA ula_prefix=\"${ula_prefix}\" seems invalid. Please verify that a ula_prefix is set and valid."
        return 1
    fi
}
 
ip6t() {
    ip6tables "${@}"
}
 
ip6t_add() {
    if ! ip6t -C "${@}" &> /dev/null; then
        ip6t -I "${@}"
    fi
}
 
nat6_init() {
    iptables-save -t nat \
    | sed -e "/\s[DS]NAT\s/d;/\sMASQUERADE$/d;/\s--match-set\s\S*/s//\06/" \
    | ip6tables-restore -T nat
}
 
masq6_network() {
    # ${config} contains the ID of the current section
    local network_name="${1}"
 
    local device
    network_get_device device "${network_name}" || return 0
 
    local done_net_dev
    for done_net_dev in ${DONE_NETWORK_DEVICES}; do
        if [ "${done_net_dev}" = "${device}" ]; then
            log "Already configured device=\"${device}\", so leaving as is."
            return 0
        fi
    done
 
    log "Found device=\"${device}\" for network_name=\"${network_name}\"."
 
    if [ "${zone_masq6_privacy}" -eq 1 ]; then
        log "Enabling IPv6 temporary addresses for device=\"${device}\"."
 
        log "Accepting router advertisements on ${device} even if forwarding is enabled (required for temporary addresses)"
        echo 2 > "/proc/sys/net/ipv6/conf/${device}/accept_ra" \
        || log "Error: Failed to change router advertisements accept policy on ${device} (required for temporary addresses)"
 
        log "Using temporary addresses for outgoing connections on interface ${device}"
        echo 2 > "/proc/sys/net/ipv6/conf/${device}/use_tempaddr" \
        || log "Error: Failed to enable temporary addresses for outgoing connections on interface ${device}"
    fi
 
    append DONE_NETWORK_DEVICES "${device}"
}
 
handle_zone() {
    # ${config} contains the ID of the current section
    local config="${1}"
 
    local zone_name
    config_get zone_name "${config}" name
 
    # Enable masquerading via NAT6
    local zone_masq6
    config_get_bool zone_masq6 "${config}" masq6 0
 
    log "Firewall config=\"${config}\" zone=\"${zone_name}\" zone_masq6=\"${zone_masq6}\"."
 
    if [ "${zone_masq6}" -eq 0 ]; then
        return 0
    fi
 
    # IPv6 privacy extensions: Use temporary addrs for outgoing connections?
    local zone_masq6_privacy
    config_get_bool zone_masq6_privacy "${config}" masq6_privacy 1
 
    log "Found firewall zone_name=\"${zone_name}\" with zone_masq6=\"${zone_masq6}\" zone_masq6_privacy=\"${zone_masq6_privacy}\"."
 
    log "Setting up masquerading nat6 for zone_name=\"${zone_name}\" with zone_masq6_privacy=\"${zone_masq6_privacy}\""
 
    local ula_prefix="$(get_ula_prefix)"
    validate_ula_prefix "${ula_prefix}" || return 1
 
    local postrouting_chain="zone_${zone_name}_postrouting"
    log "Ensuring ip6tables chain=\"${postrouting_chain}\" contains our MASQUERADE."
    ip6t_add "${postrouting_chain}" -t nat \
        -m comment --comment "!fw3" -j MASQUERADE
 
    local input_chain="zone_${zone_name}_input"
    log "Ensuring ip6tables chain=\"${input_chain}\" contains our permissive DNAT rule."
    ip6t_add "${input_chain}" -t filter -m conntrack --ctstate DNAT \
        -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
 
    local forward_chain="zone_${zone_name}_forward"
    log "Ensuring ip6tables chain=\"${forward_chain}\" contains our permissive DNAT rule."
    ip6t_add "${forward_chain}" -t filter -m conntrack --ctstate DNAT \
        -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
 
    local DONE_NETWORK_DEVICES=""
    config_list_foreach "${config}" network masq6_network
 
    log "Done setting up nat6 for zone=\"${zone_name}\" on devices: ${DONE_NETWORK_DEVICES}"
}

restart_odhcp() {
	log "Restarting ODHCP v6 to fix clients not getting V6 addresses"
	/etc/init.d/odhcpd restart
}
 
main() {
    nat6_init
    config_load firewall
    config_foreach handle_zone zone
    restart_odhcp
}
 
main "${@}"

force_link = 1 did not solve the race condition.

root@meow:~# uci show network.lan
network.lan=interface
network.lan.type='bridge'
network.lan.ifname='eth0'
network.lan.proto='static'
network.lan.netmask='255.255.255.0'
network.lan.ip6assign='60'
network.lan.force_link='1'
network.lan.ipaddr='192.168.44.1'
network.lan.ipv6='on'

For the record, the logs you have there are not related to odhcp server part, but the odhcpc which is the client part. The lan hosts don't get any IPv6 address? Not even ULA? Does the wan6 get the IPv6 correctly?

1 Like

The router WAN obtains IPv6 from upstream successfully and can ping6 google.com without issues.

The clients on LAN are given a ULA IPv6 that can ping the router on V6 but no internet routing works on IPv6.

My test client on LAN is Windows 10 and has IPv6 privacy extensions enabled. The client gets multiple V6 addresses from the router but it seems like it may not be routing any traffic until odhcpd is restarted which is odd - because router has all routing it needs as seen in ping6 tests.

Since hosts get the ULA from OpenWrt it doesn't look like odhcp issue and maybe restarting it does initiate a sequence of events which solves the problem.
Check the routing table of the lan host. Does it have a default route?
You may also want to try ra_default option 1 from default 0 in dhcp.lan, since you are not advertising GUA.

1 Like

Facing the same problems currently, I get an ipv6 but don't get ipv6pd until I restart the interface manually.