Skip to content

Asuswrt-Merlin: modernize dnsmasq/SDN integration and harden service lifecycle #342

Open

Description

@maghuro

Problem

The current Asuswrt-Merlin integration has several lifecycle/ownership assumptions that become fragile on newer Merlin 3006 firmware and when other addons or user scripts coexist with ctrld.

The most visible symptom is related to Guest Network Pro / SDN (see #229), but the underlying integration concerns are broader:

  • ctrld can manage shared Merlin hook files too aggressively instead of owning only its own clearly delimited content;
  • the historical dnsmasq integration relies on complete /jffs/configs/dnsmasq*.conf snapshots, which can freeze firmware-generated state and does not map cleanly to Merlin 3006 SDN instances;
  • Merlin 3006 provides dedicated supported SDN extension points such as dnsmasq-sdn.postconf;
  • interrupted setup/cleanup or concurrent addon/user changes need conservative, retryable ownership handling;
  • the generated Merlin service/startup lifecycle should not trust stale/reused PIDs, lose installed argv during self-upgrade, or leave partially published shared/private scripts.

Proposed direction

Shared dnsmasq hooks

  • own only an explicit ctrld block inside existing shared hooks;
  • preserve unrelated content, ordering, modes and existing hook ownership;
  • migrate the legacy ctrld-generated hook format without deleting surrounding user/addon content;
  • avoid terminating the enclosing shared hook with ctrld-owned exit 0;
  • update shared files atomically and revalidate them before replacement.

Merlin 3006 / SDN

  • use both dnsmasq.postconf and dnsmasq-sdn.postconf;
  • verify the generated runtime dnsmasq configuration after restart;
  • fail closed when unresolved included configuration could add alternate upstreams;
  • never create full dnsmasq-N.conf replacements for SDN instances;
  • keep a compatibility fallback only for the main dnsmasq when the supported hook demonstrably did not apply;
  • migrate old ctrld-created snapshots conservatively with crash/retry-safe ownership state.

Service lifecycle

  • publish/migrate ctrld.startup atomically without clobbering unrelated files;
  • preserve exact installed argument boundaries;
  • validate PID ownership through /proc/<pid>/cmdline before status/stop actions;
  • serialize Merlin start/stop/restart/uninstall and the real CLI upgrade restart transaction;
  • make self-upgrade migration execute from the newly launched binary;
  • keep shared services-start / service-event edits idempotent and ownership-safe;
  • validate the dnsmasq PID before the diskmon service-event signal path.

Scope

This is intended for the maintained v1.x router-support line.

The work is deliberately separated from unrelated router backends and preserves the v1.x dnsmasq package's existing template behavior.

Related

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. matrix8967 commented on Sep 30, 2026

    @matrix8967
    Collaborator

    Heya,

    Thanks for the issue and the subsequent PR. 🙏 There's lots of solid QoL improvements here.

    I'll have a look at this after the upcoming 1.5.8 release is out and we'll go from there.

    🖤 Thanks again!

  3. maghuro commented on Oct 7, 2026

    @maghuro
    Author

    Heya,

    Thanks for the issue and the subsequent PR. 🙏 There's lots of solid QoL improvements here.

    I'll have a look at this after the upcoming 1.5.8 release is out and we'll go from there.

    🖤 Thanks again!

    Small real-world datapoint from today, now that v1.5.8 is tagged.

    On Merlin 3006 I changed dhcp_staticlist (removed a few stale reservations and added a new one) and then ran the normal restart_dnsmasq path. NVRAM immediately reflected the new list, but the runtime /etc/dnsmasq.conf was still being sourced from ctrld's existing /jffs/configs/dnsmasq.conf snapshot, so it temporarily kept the deleted reservations and did not include the new one.

    A later ctrld lifecycle pass refreshed the snapshot and the runtime config caught up.

    So this ended up being a concrete example of the "complete dnsmasq snapshot can freeze firmware-generated state" problem described in this issue. The hook-based path in #343 should avoid that stale window by letting Merlin regenerate from the current NVRAM state first and then applying ctrld's changes.

    No action needed from me here, just adding a real-world data point now that 1.5.8 is out. Thanks again!

  4. matrix8967 commented on Oct 19, 2026

    @matrix8967
    Collaborator

    Heya @maghuro,

    Wanted to follow up properly. I spent some time on my own GT-AX6000 (Merlin 3006.102.9_0) going through everything here.

    Stale dnsmasq snapshot:​ reproduced on stock 1.5.8.

    • I removed a DHCP reservation in the GUI, and NVRAM updated.
    • After service restart_dnsmasq, /etc/dnsmasq.conf still had the old dhcp-host line from ctrld's /jffs/configs/dnsmasq.conf copy.
    • It only went away after ctrld restart refreshed that copy.

    #347:​ tested on the same router.

    • On 1.5.8, a Network Map name like Name 2.5Gbps comes through as Name 2.
    • With your change, the full name shows up locally, is sent upstream, and the existing client shows the new name in the Control D dashboard.

    #343:​ I haven't validated it on hardware yet, but we've gone through it and the approach makes a lot of sense.

    Next steps:​ because of how our internal development works, we can't merge PRs here directly. Instead, we'll bring your changes in on our side, put them through our usual review and testing, and credit you in the commit and release notes. No ETA yet.

    There's nothing more you need to do. If you want to follow along or weigh in as we go, you're very welcome to, but no pressure at all.

    'ppreciate the help and all this is good work. I recognize your username from some other AsusWRT-Merlin bugs we've seen in the past, so we very much appreciate your efforts. 🖤

  5. maghuro commented on Oct 19, 2026

    @maghuro
    Author

    Hey @matrix8967,

    Thank you so much for taking the time to test all of this on your own GT-AX6000 and for such a detailed follow-up. I really appreciate it!

    It's especially encouraging to see both issues reproduced and the fixes heading towards an official release.

    I completely understand the internal development process, and I'm more than happy to leave things in your hands from here.

    And thanks for the kind words! It's always a pleasure to contribute, especially when it helps make things better for the Asuswrt-Merlin community.

    Looking forward to seeing these improvements land!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions