What's new

DDNS update loop - caused by watchdog (??)

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

bengalih

Senior Member
I use a DDNS custom update script to update to Cloudflare. For the most part it works find. However multiple times a day it seems that the DDNS service gets in some sort of loop and even though it properly registers the address it continues to do so.

My script uses a "throttle" function so as to not overload the DNS servers with constant attempts. It does this by checking the logfile it generates and if it had a successful update within the last 300 seconds, the script will not attempt an update and will log a throttled event.

Here is an example of my ddns events from syslog, it shows 2 proper events, followed by the loop cycle, followed by one more good event (each separated by a blank line):

Code:
Apr  6 14:43:18 rc_service: dhcp6c 18256:notify_rc restart_ddns
Apr  6 14:43:18 custom_script: Running /jffs/scripts/service-event (args: restart ddns)
Apr  6 14:43:18 start_ddns: update CUSTOM , wan_unit 0
Apr  6 14:43:18 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 14:43:20 ddns: Completed custom ddns update
Apr  6 14:43:20 custom_script: Running /jffs/scripts/service-event-end (args: restart ddns)

Apr  6 15:13:18 rc_service: dhcp6c 23328:notify_rc restart_ddns
Apr  6 15:13:18 custom_script: Running /jffs/scripts/service-event (args: restart ddns)
Apr  6 15:13:18 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:13:18 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:13:20 ddns: Completed custom ddns update
Apr  6 15:13:20 custom_script: Running /jffs/scripts/service-event-end (args: restart ddns)

Apr  6 15:43:18 rc_service: dhcp6c 28346:notify_rc restart_ddns
Apr  6 15:43:18 custom_script: Running /jffs/scripts/service-event (args: restart ddns)
Apr  6 15:43:19 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:43:19 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:43:20 watchdog: start ddns.
Apr  6 15:43:20 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:43:20 rc_service: waitting "restart_ddns" via  ...
Apr  6 15:43:20 ddns: Completed custom ddns update
Apr  6 15:43:20 custom_script: Running /jffs/scripts/service-event-end (args: restart ddns)
Apr  6 15:43:21 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:43:22 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:43:22 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:43:22 ddns: Custom ddns update failed
Apr  6 15:43:22 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:43:51 watchdog: start ddns.
Apr  6 15:43:51 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:43:51 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:43:52 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:43:52 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:43:52 ddns: Custom ddns update failed
Apr  6 15:43:52 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:44:21 watchdog: start ddns.
Apr  6 15:44:21 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:44:21 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:44:22 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:44:22 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:44:22 ddns: Custom ddns update failed
Apr  6 15:44:22 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:44:51 watchdog: start ddns.
Apr  6 15:44:51 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:44:51 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:44:52 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:44:52 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:44:52 ddns: Custom ddns update failed
Apr  6 15:44:52 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:45:21 watchdog: start ddns.
Apr  6 15:45:21 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:45:21 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:45:22 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:45:22 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:45:22 ddns: Custom ddns update failed
Apr  6 15:45:22 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:45:51 watchdog: start ddns.
Apr  6 15:45:51 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:45:51 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:45:52 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:45:52 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:45:52 ddns: Custom ddns update failed
Apr  6 15:45:52 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:46:21 watchdog: start ddns.
Apr  6 15:46:21 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:46:21 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:46:22 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:46:22 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:46:22 ddns: Custom ddns update failed
Apr  6 15:46:22 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:46:51 watchdog: start ddns.
Apr  6 15:46:51 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:46:51 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:46:52 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:46:52 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:46:52 ddns: Custom ddns update failed
Apr  6 15:46:52 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:47:21 watchdog: start ddns.
Apr  6 15:47:21 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:47:21 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:47:22 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:47:22 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:47:22 ddns: Custom ddns update failed
Apr  6 15:47:22 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:47:51 watchdog: start ddns.
Apr  6 15:47:51 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:47:51 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:47:52 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:47:52 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:47:52 ddns: Custom ddns update failed
Apr  6 15:47:52 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)
Apr  6 15:48:21 watchdog: start ddns.
Apr  6 15:48:21 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:48:21 custom_script: Running /jffs/scripts/service-event (args: start ddns)
Apr  6 15:48:22 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:48:22 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:48:23 ddns: Completed custom ddns update
Apr  6 15:48:23 custom_script: Running /jffs/scripts/service-event-end (args: start ddns)

Apr  6 16:13:18 rc_service: dhcp6c 1405:notify_rc restart_ddns
Apr  6 16:13:18 custom_script: Running /jffs/scripts/service-event (args: restart ddns)
Apr  6 16:13:19 start_ddns: update CUSTOM , wan_unit 0
Apr  6 16:13:19 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 16:13:20 ddns: Completed custom ddns update
Apr  6 16:13:20 custom_script: Running /jffs/scripts/service-event-end (args: restart ddns)

It appears that every 30 minutes I must be getting a new dhcp IPv6 lease as the dhcp6c service triggers a restart_ddns event.
This kicks off the service-event script (of which I have no code for this event so it should not do anything).
Then start_ddns kicks off my ddns-start script passing my address (IPv4) to it.
The log then shows it completed the update
Finally a service-event-end is kicked off (of which I also have no code for ddns in).

That all looks pretty normal to me. The issue is the event that starts at 15:43:18 above.
You can see this process is interrupted by a watchdog event. Even so it appears to complete:
Code:
Apr  6 15:43:20 ddns: Completed custom ddns update

But then it appears to restart the process over and over again multiple times over the next 5 minutes.
It fails because it is getting throttled and only succeeds again after the throttle timeout.
This is further reflected in my ddns-start script log (custom) where it shows the following entries corresponding the log above:

Code:
2023-04-06 14:43:20     UPDATE_SUCCESS  {"result:{(data removed}}
2023-04-06 15:13:20     UPDATE_SUCCESS  {"result:{(data removed}}
2023-04-06 15:43:20     UPDATE_SUCCESS  {"result:{(data removed}}
2023-04-06 15:43:22     UPDATE_THROTTLED
2023-04-06 15:43:52     UPDATE_THROTTLED
2023-04-06 15:44:22     UPDATE_THROTTLED
2023-04-06 15:44:52     UPDATE_THROTTLED
2023-04-06 15:45:22     UPDATE_THROTTLED
2023-04-06 15:45:52     UPDATE_THROTTLED
2023-04-06 15:46:22     UPDATE_THROTTLED
2023-04-06 15:46:52     UPDATE_THROTTLED
2023-04-06 15:47:22     UPDATE_THROTTLED
2023-04-06 15:47:52     UPDATE_THROTTLED
2023-04-06 15:48:23     UPDATE_SUCCESS  {"result:{(data removed}}
2023-04-06 16:13:20     UPDATE_SUCCESS  {"result:{(data removed}}

So you can see the normally functioning successful updates at 14:43 and 15:13, and then another successful one at 15:43.
And even though this registers as successful, the throttle events correspond to all the watchdog loops above.
Finally the 5 minute loop ends at 15:48.
Then the next normal event happens 30 mins later at 16:13.

Can anyone lend some insight into what is misbehaving during these instances of watchdog interfering with this process (if that is what is going on...)?

thanks
 
I use a DDNS custom update script to update to Cloudflare. For the most part it works find. However multiple times a day it seems that the DDNS service gets in some sort of loop and even though it properly registers the address it continues to do so.

Why not just use dns-o-matic (not sure if they support AAAA records though) or one of the premade merlin scripts out there for cloudflare that have proven quite reliable?

If your ISP is giving you a new IPv6 lease every 30 minutes, you've got one lousy ISP. You sure something else isn't going on with your setup that is causing the ISP to do this? Is your LAN v6 subnet changing too (that would be a real PITA if so)? Perhaps DHCPv6 requests are getting forwarded upstream to them causing their server to get confused.
 
I actually don't need AAAA record support, so dns-o-matic might work. I don't know if this was available when I set this up initially - I never used that service before, but just looked at it and it appears that it supports Cloudflare. I see that www.dnsomatic.com is built into the ddns client list is Merlin, so I assume that's what I choose.

The script I use was not one I wrote (but did contribute to) here:

I'm not sure this is a script issue though. The script and process is all pretty straight forward. When the update succeeds it sends
Code:
/sbin/ddns_custom_updated 1
Which should be sufficient to end the update process.

If you look at the logs above it is clear that it is doing this, the following line is a direct result of that from the output of ddns_custom_updated:
Code:
Apr  6 14:43:20 ddns: Completed custom ddns update

And 4 times out of 5 that seems to be the end of it. But in the problematic entry above you can see that even though that event is logged...

Code:
Apr  6 15:43:18 rc_service: dhcp6c 28346:notify_rc restart_ddns
Apr  6 15:43:18 custom_script: Running /jffs/scripts/service-event (args: restart ddns)
Apr  6 15:43:19 start_ddns: update CUSTOM , wan_unit 0
Apr  6 15:43:19 custom_script: Running /jffs/scripts/ddns-start (args: XX.XX.XX.XX)
Apr  6 15:43:20 watchdog: start ddns.
Apr  6 15:43:20 rc_service: watchdog 396:notify_rc start_ddns
Apr  6 15:43:20 rc_service: waitting "restart_ddns" via  ...
Apr  6 15:43:20 ddns: Completed custom ddns update

...for some reason that watchdog event is entering in there and apparently somehow restarting the ddns update process again even though it just completed successfully.

I don't believe there is anything wrong with the script (more on that in a second). And I can't totally answer your "why every 30 minutes" yet as I am trying to gather a bit more info to give you a better answer. Regardless, it isn't like it is happening every 3 seconds...at 30 minutes it isn't like anything is getting taxed on the router, etc that would account for this. So whether it was triggering every 30 minutes, or 4 hours I'd expect the output to be the same.

Now, I fully acknowledge that the way the script is written could be showing this error more prominently than another alternative that may actually mask this issue. The reason is the script uses this throttling mechanism. Without the throttling mechanism, what one would probably see is just one issue of a double registration. The reason I see so many of these:

Code:
Apr  6 15:43:22 ddns: Custom ddns update failed

Is because after the first one (at the 30 minute interval) succeeds, all additional attempts for 300 seconds will throttle, which will trigger the failure event (which is technically true because it did fail to update on a throttled attempt). So the ddns client continues to try time and again for 5 minutes until it can succeed without being throttled.

A script that just updated every single time it was called would just show success attempts in a row when this problem occurs. But that doesn't mean that a problem isn't occurring. Something (watchdog?) is triggering a double event in some cases and I would like to understand what is happening.

Moving to the dnsomatic might get rid of this, and might be the best option for now (as I don't need the AAAA records). Also changing the script to either set a lower throttle threshold, not throttle at all, or report a success on throttle would also cut down on the messages, but wouldn't stop the fact that a double event is sometimes being triggered.

Really, what I need to understand here is exactly what watchdog's purpose is and why it would be jumping in only sometimes on these requests which is what appears to be causing the loop.
 
Really, what I need to understand here is exactly what watchdog's purpose is and why it would be jumping in only sometimes on these requests which is what appears to be causing the loop.

It appears that getting a new WAN v6 IP is causing a DDNS update to fire, which I'd expect. Either that or your script is telling it to update every 30 mins no matter what.

As to why it is attempting to update twice, maybe sometimes both your v6 and v4 update and it needs to do twice, but since you've throttled it, it can't, so it keeps retrying until it can. Or your throttling is confusing the DDNS process due to blocking it so it restarts the process thinking it is having a problem, which fires a new update, which you are throttling.

Either that or sometimes it just does a second update for some unknown reason, a bug, or even though it says success something actually did fail. If you remove your throttle I'm guessing you'll probably only see 2 updates max each time it tries.

I use the built in DDNS with dyndns and it works fine, but I'm not using v6. I don't monitor the logs for it closely and my IP very rarely changes but I don't recall ever seeing more than 1 update when it does.
 
Last edited:
It appears that getting a new WAN v6 IP is causing a DDNS update to fire, which I'd expect. Either that or your script is telling it to update every 30 mins no matter what.
Yeah, I wanted to look at some packet captures before I answered for sure. My WAN setup is a little unorthodox as I am on AT&T fiber and I am bypassing their device by performing 802.11x certificate authentication using wpa_supplicant on the device. Additionally, I have had to configure a workaround where I define a static IP on the ASUS but still have to manually trigger the dhcpv6 client to properly apportion my IA_NA and IA_PD from the delegating router. If I don't do this...and just use the "Native IP" option on the ASUS, my IPv6 routing works on all my clients, but not on the router itself (i.e. the router can't reach the internet over IPv6, even though the clients can).

To remove some of this complexity for the purposes of gathering data I set my device back to normal "Native IPv6", and while it breaks my routing from the device itself (as mentioned above), it is a normal/supported configuration of the Merlin FW, so I am using that to reduce any issue introduced by my special config.

Even in "Native IPv6" mode I have confirmed that the odhcp6 client configured by the firmware is requesting a renewal every 30 minutes. This is because the preferred and valid lifetimes returned from the delegating router for both IA_NA and IA_PD are both 3600. So the client has no choice but to issue a renewal request at half that interval 1800s = 30 minutes.

Now I will note that this *might* not happen if I was not bypassing the ISP's equipment (which I do because it has all sorts of tracking built-in and because it doesn't offer a true bridge mode). It is possible that since their router would normally act as CPE that it would provide a longer lease time to my router when its "bridge" mode. In any event, if I want to bypass their equipment and have mine act as the CPE, this seems normal.
As to why it is attempting to update twice, maybe sometimes both your v6 and v4 update and it needs to do twice, but since you've throttled it, it can't, so it keeps retrying until it can. Or your throttling is confusing the DDNS process due to blocking it so it restarts the process thinking it is having a problem, which fires a new update, which you are throttling.
I considered that. But I don't know if the timing seems right, but I admittedly don't know the internal Merlin mechanics of how ddns gets triggered. My assumption is that it happens only when it (somehow) detects a change to the IPv4. There is also the "Forced update interval" that is listed in the GUI that I have set to 1 day. In reality, I probably don't need this at all because while my IP is technically dynamic, I have never seen it change, the ISP has clearly created some type of reservation for me, but I don't pay for a "static", and the possibility that it might change makes me want to keep a DDNS client running. I could almost definitely change the forced interval to longer.

EDIT: I just found where I can check my IPv4 lease time on the router. I had assumed it would have been at least 24 hours and was planning on setting up a tcpdump to capture and confirm the data, but it turns out it is only an hour. So it is possible that both v4 and v6 might occur close enough to cause this issue, but I'm still not sure the timing in the logs is correct.

The reason is that when I get the error - remember the first entry is successful, but the throttle loop begins always within 1-2 seconds of that. I don't think it is possible that it is always happening that close to each other to cause this. Still I think I will try to tcpdump for a day or two and see if the request times for IPv4 overlap this often and this close....that could be it.

Either that or sometimes it just does a second update for some unknown reason, a bug, or even though it says success something actually did fail. If you remove your throttle I'm guessing you'll probably only see 2 updates max each time it tries.

I use the built in DDNS with dyndns and it works fine, but I'm not using v6. I don't monitor the logs for it closely and my IP very rarely changes but I don't recall ever seeing more than 1 update when it does.
Yes, that's what I recommended above, that if the throttle was removed I expect I'd just see the two.
I might just switch to try scrip-o-matic, but I'm not sure that would resolve it (esp. if the ipv4/6 conflict).

I'm still interested in under normal circumstances when a line like this would be fired, because it appears to not be part of the normal process every 30 minutes, only once in a while to cause the loop:
Code:
Apr  6 15:43:20 watchdog: start ddns.

This is also in contrast with the following line which seemingly is triggered by the forced update option in the GUI:
Code:
Apr  6 17:42:51 watchdog: Forced DDNS update (after 1 days)
 
Even in "Native IPv6" mode I have confirmed that the odhcp6 client configured by the firmware is requesting a renewal every 30 minutes. This is because the preferred and valid lifetimes returned from the delegating router for both IA_NA and IA_PD are both 3600. So the client has no choice but to issue a renewal request at half that interval 1800s = 30 minutes.

Even with a 1 hour lease time you should typically keep the same IPs when you renew. Is there anything on this router you haven't totally overridden or hacked with scripts?

You do realize they can track you just as well whether you have their router or not - in fact much easier for them to gather statistics on their user base at a central location. Just put their router in passthrough mode and eliminate that craziness. If you are truly paranoid about it use a VPN as that will actually bypass their snooping unlike what you're doing now. Or use DoT as probably all they're tracking is your DNS lookups anyway.
 
Asus`IPv6 DDNS implementation still has issues. For instance it will rerun inadyn everytime odhcp6c tries to refresh your delegated prefix, which might lead to excessive DDNS updates. They probably haven`t implemented caching yet like they did for IPv4. I don't recommend using it.

In 388.2 I will at least alleviate the issue by ensuring that if DDNS IPv6 updates are disabled, then it will no longer keep re-running inadyn on every prefix refresh. A proper fix will need to come from Asus, probably in the form of implementing proper caching of the IPv6 WAN address.
 
...
My script uses a "throttle" function so as to not overload the DNS servers with constant attempts. It does this by checking the logfile it generates and if it had a successful update within the last 300 seconds, the script will not attempt an update and will log a throttled event.
...
...
It appears that every 30 minutes I must be getting a new dhcp IPv6 lease as the dhcp6c service triggers a restart_ddns event.
This kicks off the service-event script (of which I have no code for this event so it should not do anything).
Then start_ddns kicks off my ddns-start script passing my address (IPv4) to it.
The log then shows it completed the update
Finally a service-event-end is kicked off (of which I also have no code for ddns in).
...
That all looks pretty normal to me. The issue is the event that starts at 15:43:18 above.
You can see this process is interrupted by a watchdog event.
...
But then it appears to restart the process over and over again multiple times over the next 5 minutes.
It fails because it is getting throttled and only succeeds again after the throttle timeout.
If the 2nd & subsequent calls to the ddns-start script have the same IPv4 address as the 1st call, throttling is the right thing to do for the reasons you mentioned; however, if the script is always reporting a "throttled event" as a "DDNS failure" regardless of the context of the call, it's essentially triggering more calls as the built-in service continues to try and force an update.

IMO, the throttling function should also double-check if the new "WAN IP" argument matches the IP address from the previously successful DDNS update; if the IP matches, report it as a "successful" DDNS update (which is technically true because there is no change in WAN IP); otherwise, it's a failure. Doing it that way might just be what the 2nd call needs to prevent the repeated calls which, as mentioned, are likely just trying to force an update due to the reported "failure" by the throttling function.

Here is some "pseudo-code" with a possible solution:
Bash:
#!/bin/sh
# FILENAME: ddns-start
...
...
newWAN_IP4addr="$1"
...
...
## Check if new WAN IP address matches last registered WAN IPs ##
_CheckForMatched_WAN_IPv4_Address_()
{
    FoundMatchWANIPaddr=true
    WAN_IPaddr1="$(nvram get ddns_ipaddr)"
    WAN_IPaddr2="$(nvram get wan0_ipaddr)"
    WAN_IPaddr3="$(nvram get wan0_realip_ip)"

    if [ -n "$WAN_IPaddr1" ] && [ "$WAN_IPaddr1" != "$1" ]
    then FoundMatchWANIPaddr=false ; fi

    if [ -n "$WAN_IPaddr2" ] && [ "$WAN_IPaddr2" != "$1" ]
    then FoundMatchWANIPaddr=false ; fi

    if [ -n "$WAN_IPaddr3" ] && [ "$WAN_IPaddr3" != "$1" ]
    then FoundMatchWANIPaddr=false ; fi

    "$FoundMatchWANIPaddr" && return 0 || return 1
}

_DoThrottling_()
{
   ## Code that checks for the threshold conditions to do throttling ##
   ...
   ...
   if [ "$ThrottlingNeeded" = "FALSE" ]
   then
        echo "Continuing with script." ; return 1
   fi

   if _CheckForMatched_WAN_IPv4_Address_ "$newWAN_IP4addr"
   then
        echo "New WAN IP [$newWAN_IP4addr] matches previously updated WAN IP."
        Report_DDNS_Update SUCCESS
   else
        echo "New WAN IP [$newWAN_IP4addr] does NOT match previously updated WAN IP."
        Report_DDNS_Update FAILURE
   fi
   echo "Throttling is needed. Exiting..."
   exit 1
}
...
...
_DoThrottling_
...
...
## Continue with the rest of the script ##
...
...
#EOF#

To avoid making the 3 calls to the nvram command you could instead cache the last successful WAN IP address in your own logfile so that when needed you can simply grep for it to compare against the script argument. There are several ways to store the WAN IPv4 address, but these are just some ideas off the top of my head.

My 2 cents.
 
Last edited:
If the 2nd & subsequent calls to the ddns-start script have the same IPv4 address as the 1st call, throttling is the right thing to do for the reasons you mentioned; however, if the script is always reporting a "throttled event" as a "DDNS failure" regardless of the context of the call, it's essentially triggering more calls as the built-in service continues to try and force an update.

IMO, the throttling function should also double-check if the new "WAN IP" argument matches the IP address from the previously successful DDNS update; if the IP matches, report it as a "successful" DDNS update (which is technically true because there is no change in WAN IP); otherwise it's a failure. Doing it that way might just be what the 2nd call needs to prevent the repeated calls which, as mentioned, are likely just trying to force an update due to the reported "failure" by the throttling function.

I was considering the exact same thing when I started thinking about how to make the issue better. I thought about turning the throttle off entirely, but then entertained your idea of querying first to see what the current registered IP was and if it matched, then simply do no update and mark as successful anyway.

But this doesn't work exactly with the purpose of the script (at least as it was designed). The original author put the throttling in there to deal with limiting API calls against CF's TOS. I'm not sure what they were originally, but the best info I can find right now states:

The global rate limit for Cloudflare's API is 1200 requests per five minutes

I'm pretty sure that the call to update the record is only a single API call, so even if a script was misbehaving, I would think with the network latency it might be hard to make that many calls. If you even just put in a single .3 second sleep in the script either before or after the call it would be impossible to hit that limit - and that's worst case scenario.

However, if you were concerned about a misbehaving script hitting that limit, then the Throttle idea is good. However using that methodology, introducing a query into the script to first query the API to find out the registered address before updating would still mean hitting the API. Therefore a misbehaving script could still trigger a lot of API calls.

But your suggestions make me think further that indeed we don't need to do an API call per-se. We could just look at a log entry to decide. Or, we could even do a ping or name lookup on the current address to see if it matches. All of these are possible solution that each have their own benefits and drawbacks (i.e. what happens if log file gets deleted? what happens if network condition prevents resolution of current address...might need a lot of error code).

One also has to think of where should the processing/network power be minimized? For instance, by simply changing the throttle or delay down to a second this issue would be minimized. A misbehaving script would still not hit the TOS limit, but it could come close. Is it acceptable to bombard them with 100 requests of update per minute even if it is negligible processing/bandwidth on my side? Someone might not even notice it unless you examined logs, and yet it is probably poor etiquette to send them so many requests when 1 would do.

In the short term, I have lessened the throttle to 5 seconds. This does not solve the fact that ddns watchdog is still triggering a second attempt (nor would your solution of checking), but it does at least prevent a loop so that when the rogue second attempt happens it gets resolved immediately. In the long term, I do think checking the IP first is the best way to do it.
 
Just had a question about your code, in case I decide to go this route:

Bash:
#!/bin/sh
# FILENAME: ddns-start
...
...
newWAN_IP4addr="$1"
...
...
## Check if new WAN IP address matches last registered WAN IPs ##
_CheckForMatched_WAN_IPv4_Address_()
{
    FoundMatchWANIP=true
    WAN_IPaddr1="$(nvram get ddns_ipaddr)"
    WAN_IPaddr2="$(nvram get wan0_ipaddr)"
    WAN_IPaddr3="$(nvram get wan0_realip_ip)"

    if [ -n "$WAN_IPaddr1" ] && [ "$WAN_IPaddr1" != "$1" ]
    then FoundMatchWANIP=false ; if

    if [ -n "$WAN_IPaddr2" ] && [ "$WAN_IPaddr2" != "$1" ]
    then FoundMatchWANIP=false ; if

    if [ -n "$WAN_IPaddr3" ] && [ "$WAN_IPaddr3" != "$1" ]
    then FoundMatchWANIP=false ; if

    "$FoundMatchWANIP" && return 0 || return 1
}

_DoThrottling_()
{
   ## Code that checks for the threshold conditions to do throttling ##
   ...
   ...
   if [ "$ThrottlingNeeded" = "FALSE" ]
   then
        echo "Continuing with script." ; return 1
   fi

   if _CheckForMatched_WAN_IPv4_Address_ "$newWAN_IP4addr"
   then
        echo "New WAN IP matches previously updated WAN IP."
        Report_DDNS_Update SUCCESS
   else
        echo "New WAN IP does NOT match previously updated WAN IP."
        Report_DDNS_Update FAILURE
   fi
   echo "Throttling is needed. Exiting..."
   exit 1
}
...
...
_DoThrottling_
...
...
## Continue with the rest of the script ##
...
...
#EOF#

How exactly are you determining with those variables what the currently configured WAN IP is vs what it has been updated to.
On my box wan0_ipaddr and wan0_realip_ip both have my current IP. ddns_ipaddr does not even exist in nvram.
Was this a variable you were making up to store in, something that gets set temporarily and then removed during ddns, etc?

If my WAN IP is 20.30.40.50, and my address refreshes say to 20.30.40.60, doesn't my WAN interface get updated first and then ddns kicks off to send that new address to the update script? I'm not following where I would be able to check what my recently replaced IP address was using nvram. IOW, ddns isn't waiting to update the address before passing it to the WAN interface, it detects a change in the WAN interface which triggers it.

I'm on board 100% with storing it in the log file and finding it from there, or doing some type of name resolution to query the currently returned address from an external source, I'm just not following along with how you are detecting what the old address is from NVRAM.
 
How exactly are you determining with those variables what the currently configured WAN IP is vs what it has been updated to.
I'm not quite sure I understand your question. I'm not trying to determine "what the currently configured WAN IP is vs what it has been updated to." After a DDNS update has been successfully completed, all 3 NVRAM variables (ddns_ipaddr, wan0_ipaddr, wan0_realip_ip) have the same value: the most recently registered WAN IP address that also matches the records of the DDNS Service that was called during the update.

At least this has been my experience for several years with the ASUS routers I've worked with (RT-AC86Us, RT-AC68Us, GT-AC5300, RT-AC5300, and lately with an RT-AX86S). IOW, the "currently configured WAN IP" on the router is the same as the WAN IP address that the DDNS service has been updated to. This, of course, assumes that your ISP is giving you an actual public IP address and that your ASUS router is not set up in a double-NAT scenario with some ISP modem/router combo in the middle acting as the network gateway.

IOW, the "WAN IP address" check inside the throttling function is comparing the most recently updated & cached WAN IPs against the script argument value which is intended to be used for another DDNS update.

On my box wan0_ipaddr and wan0_realip_ip both have my current IP. ddns_ipaddr does not even exist in nvram.
Was this a variable you were making up to store in, something that gets set temporarily and then removed during ddns, etc?
Are you sure that the NVRAM variable does not exist at all? Could it be that it does exist but it simply has a null (i.e. empty string) value?

You can double-check the 3 NVRAM vars with the following command:
Bash:
nvram show 2>/dev/null | grep -E "ddns_ipaddr=|wan0_ipaddr=|wan0_realip_ip="
If in fact the NVRAM variable "ddns_ipaddr" doesn't exist in your router, it would be very odd because, again, in all routers that I have access to the 3 NVRAM variables have been present and reflect the current WAN IP address every time, as described above.

Also, note that all 3 NVRAM variables exist in the stock OEM F/W as well, so they're not specific to RMerlin's F/W and are certainly not temporary (i.e. they persist after the DDNS update is completed).

According to your "signature" info, you have an RT-AC68U running the 386.3_2 F/W version which is fairly "old" (from 2021-Aug-06) so that's one main difference with all routers I've listed because they're running at minimum the 386.7_2 F/W or later versions, and the latest stock OEM version for the GT-AC5300.

Here's a screenshot of an RT-AC68U router showing all 3 NVRAM vars:

RT-AC68U_WAN_IPs.jpg


Another difference is that all routers listed have been set up with one of the pre-set DDNS services (WWW.NO-IP.COM, WWW.DYNDNS.ORG, FREEDNS.AFRAID.ORG) whereas your router is running the "CUSTOM" setup. So I suppose that might be a reason why the "ddns_ipaddr" NVRAM variable may not be automatically set or updated accordingly (but I don't really know - that's just speculation on my part).

If my WAN IP is 20.30.40.50, and my address refreshes say to 20.30.40.60, doesn't my WAN interface get updated first and then ddns kicks off to send that new address to the update script? I'm not following where I would be able to check what my recently replaced IP address was using nvram. IOW, ddns isn't waiting to update the address before passing it to the WAN interface, it detects a change in the WAN interface which triggers it.
The key is to check the "WAN IP address" argument given to the script against the cached NVRAM values *within* the scope of the throttling function and only *after* a successful DDNS update has occurred (in which case all NVRAM vars would be in sync) so that only the subsequent calls that fall within the throttling threshold (e.g. 30 seconds) go thru the "_CheckForMatched_WAN_IPv4_Address_()" function to determine whether to report/mark it as a SUCCESS or FAILURE of the DDNS update (i.e. /sbin/ddns_custom_updated [1 | 0]). This assumes that your ISP is not actually changing your WAN IP address every few seconds or minutes, of course.

Doing the checking outside the scope of the throttling function and before a successful DDNS update would probably not work because of the reasons you mentioned. In such a case, it would be better to get the last known registered WAN IP from another source (e.g. custom logfile entry, DNS name resolution) but, as you stated, that brings up another set of possible complications that need to be handled and logged accordingly.

Ultimately, you have to decide what's best for your particular situation, and what level of "good" is "good enough" & "simple enough" to maintain in the long term and which satisfies your own requirements. As I learned a long time ago, many times you have to apply the old adage: "Don't let perfect be the enemy of the good."
 
I'm not quite sure I understand your question. I'm not trying to determine "what the currently configured WAN IP is vs what it has been updated to." After a DDNS update has been successfully completed, all 3 NVRAM variables (ddns_ipaddr, wan0_ipaddr, wan0_realip_ip) have the same value: the most recently registered WAN IP address that also matches the records of the DDNS Service that was called during the update.
....

I still am not quite following you, but it may be due to the fact that indeed I don't have all these variables and thus cannot understand how you are using them:

Code:
keymaster@router-asus:/jffs/scripts# nvram show 2>/dev/null | grep -E "ddns_ipaddr=|wan0_ipaddr=|wan0_realip_ip="
wan0_ipaddr=75.X.XX.XX
wan0_realip_ip=75.X.XX.XX

Only those 2, and they are the same. So my confusion is from the fact that when my dhcp client renews my lease and is returned an IP address, I would expect that IP address to immediately be used to set those 2 values above. Let's assume the IP address changed. I no longer have access to the old IP address so I can't compare it. My guess, is that you are saying the ddns_ipaddr variable would hold that old address until it was actually updated again, and at that point I can compare the current WAN address against the old address stored therein.

I suspect that you might be correct that I use the "custom" option and this is why I don't see that variable (I think that more likely than the FW version).

In any event. In researching this issue I have discovered the inadyn built-in configuration method. I do not know why I never discovered this before and went with a custom script. I suspect that when I searched how to update Cloudflare in the past, it did not have support (or poor support) in inadyn and this is what led me to a fully 3rd party custom script. By creating an inadyn.conf file for Cloudflare, and using the "Custom" option but leveraging the built-in inadyn service, it seems that my Cloudflare updates work fine. Additionally, inadyn appears to use its own cache file. So while I suspect to still see a log entry every 30 mins when my lease renews, it shouldn't actually try the update unless there is an actual change (or after a router reboot where this log file is probably removed).

I appreciate your info. I just set this up and so far it looks great, but will have to track over the next few days and if I have to revert back I might revisit this.
 
Code:
keymaster@router-asus:/jffs/scripts# nvram show 2>/dev/null | grep -E "ddns_ipaddr=|wan0_ipaddr=|wan0_realip_ip="
wan0_ipaddr=75.X.XX.XX
wan0_realip_ip=75.X.XX.XX

Only those 2, and they are the same. So my confusion is from the fact that when my dhcp client renews my lease and is returned an IP address, I would expect that IP address to immediately be used to set those 2 values above. Let's assume the IP address changed. I no longer have access to the old IP address so I can't compare it. My guess, is that you are saying the ddns_ipaddr variable would hold that old address until it was actually updated again, and at that point I can compare the current WAN address against the old address stored therein.
Yes, that's essentially what I've observed, but of course, it all happens automatically in one sequence of events when using one of the pre-set DDNS services. But when using the "CUSTOM" setup with a custom shell script, one is responsible for taking care of all the details and getting "all your ducks in a row" to do a successful update, and then avoid unnecessary calls to the DDNS Service if the same WAN IP address is given repeatedly within a short period of time (i.e. your threshold for throttling in the custom script) while still reporting it as a "successful" DDNS update.

I suspect that you might be correct that I use the "custom" option and this is why I don't see that variable (I think that more likely than the FW version).

In any event. In researching this issue I have discovered the inadyn built-in configuration method. I do not know why I never discovered this before and went with a custom script. I suspect that when I searched how to update Cloudflare in the past, it did not have support (or poor support) in inadyn and this is what led me to a fully 3rd party custom script. By creating an inadyn.conf file for Cloudflare, and using the "Custom" option but leveraging the built-in inadyn service, it seems that my Cloudflare updates work fine. Additionally, inadyn appears to use its own cache file. So while I suspect to still see a log entry every 30 mins when my lease renews, it shouldn't actually try the update unless there is an actual change (or after a router reboot where this log file is probably removed).
Ah yes, I had completely forgotten about the built-in "Inadyn" client service which is the mechanism used under the hood when selecting from the pre-set DDNS Services. This is likely the best approach for your "CUSTOM" setup as well, and it might even be what's needed to get the NVRAM variable "ddns_ipaddr" set & updated accordingly. I hope it works well for you and am curious to know if the "missing" NVRAM variable finally appears.

Best of luck.
 
Ah yes, I had completely forgotten about the built-in "Inadyn" client service which is the mechanism used under the hood when selecting from the pre-set DDNS Services. This is likely the best approach for your "CUSTOM" setup as well, and it might even be what's needed to get the NVRAM variable "ddns_ipaddr" set & updated accordingly. I hope it works well for you and am curious to know if the "missing" NVRAM variable finally appears.

Best of luck.

Just an update for anyone else that might be interested.
The ddns_ipaddr nvram variable still doesn't exist on my box (RT-AC68U | Merlin 386.3_2) when using the CUSTOM option.
I suspect that it is because of CUSTOM, but as you state it may be firmware related. If I remember I will report back here after I get around to the FW update.

inadyn appears to work by creating files in /var/cache/inadyn that hold the last IP address that was used to send the update. This is what it checks against to see if it should or should not do an update.
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top