Midterm: Simplifying LibreMesh with OpenWrt-Native Solutions

This blog documents my ongoing progress with LibreMesh during Google Summer of Code (GSoC) 2025. Specifically, I’m focusing on integrating OpenWrt-native solutions and simplifying LibreMesh modules.

Currently, I am working on two main tasks:

  1. Replacing deferrable-reboot with watchcat: Migrating LibreMesh’s custom reboot scheduling script to the built-in OpenWrt watchcat package for improved reliability.
  2. Migrating DHCP functionality from dnsmasq to odhcpd: Transitioning DHCP and IPv6 handling to OpenWrt’s native odhcpd, currently in development and community testing.

For testing and validation, I’m using three physical routers configured to simulate realistic network conditions.

Task 1: Integrate OpenWrt’s watchcat via Hardware Detection

a. Motivation

The primary motivation for replacing LibreMesh’s deferrable-reboot script with OpenWrt’s watchcat is to leverage upstream-maintained tools, reduce redundancy, and enhance maintainability. watchcat provides robust functionality including scheduled reboots and network monitoring to automatically reboot routers in case of failure.

The design involves creating a hardware-detection (HWD) module within LibreMesh, enabling dynamic generation of watchcat configurations from user-defined UCI entries.

b. Implementation Details

The new package, lime-hwd-watchcat, is implemented in Lua, performing the following:

  • Unique Section Identification: Creates unique configuration section names prefixed with hardware identifiers to avoid conflicts.
  • Cleaning Up Configurations: Removes previously generated watchcat configurations.
  • Dynamic Configuration Generation: Reads entries from LibreMesh’s UCI configuration (hwd_watchcat) and generates corresponding /etc/config/watchcat entries.
  • Service Management: Automatically reloads watchcat after applying configuration changes using the /etc/init.d/watchcat reload command.

c. Testing & Validation

Testing involved:

  • Configuring custom UCI settings under config hwd_watchcat.
  • Validating automatic generation and updates to /etc/config/watchcat.
  • Confirming the proper removal of old configurations.
  • Checking watchcat service status and reload logs for expected behavior.

Default configuration:

Lets change it with the new package! Using:

uci add lime-node hwd_watchcat
uci set lime-node.@hwd_watchcat[-1].mode='ping_reboot'
uci set lime-node.@hwd_watchcat[-1].pinghosts='4.2.2.2'
uci set lime-node.@hwd_watchcat[-1].pingperiod='30s'
uci set lime-node.@hwd_watchcat[-1].period='6h'
uci set lime-node.@hwd_watchcat[-1].forcedelay='1m'
uci commit lime-node

And after using ‘lime-config’

d. Resources

You can see the code and the PRs for this package here:

Package (Github)

Pull Request approved

Task 2: Replace dnsmasq DHCP with odhcpd

a. Motivation

OpenWrt’s native odhcpd daemon already powers IPv6 RA/DHCPv6 and integrates tightly with ubus. The goal is to phase out dnsmasq’s DHCP functionality in favor of odhcpd, leveraging its superior IPv6 support, integration with OpenWrt, and streamlined lease handling.

The new shared-state-odhcpd_leases package watches local leases, serialises them as CRDT objects via shared-state-async, and injects remote leases back into odhcpd, giving every node the same “view” of the network.

b. Implementation Details

The new package is entirely based in Lua. Its components fall into three small groups:

  1. UCI defaults script (90_odhcpd-lease-share) – executed once at install time.
    • registers a community-scoped CRDT called odhcpd-leases, telling shared-state to refresh every two minutes and to expire entries after twenty;
    • sets two critical odhcpd options:
      leasetrigger points to our publisher script, and maindhcp='1' turns odhcpd into the sole DHCP server;
    • creates a legacy-friendly symlink /etc/ethers → /tmp/ethers.mesh;
    • finally reloads odhcpd so the new trigger takes effect.
  2. Publisher (shared-state-publish_odhcpd_leases) – called by odhcpd whenever a lease changes.
    It fetches the current lease table with ubus call dhcp ipv4leases, distils it to the minimum JSON mapping IP → {mac,hostname}, and pushes that into the CRDT bus with shared-state-async insert odhcpd-leases.
  3. Generator (shared-state-generate_odhcpd_leases) – executed on every CRDT update that the node receives.
    It writes the merged dataset to /tmp/ethers.mesh, moves the file atomically, and reloads odhcpd so the daemon immediately serves and announces the foreign leases as if they were local.

Unit tests live in tests/test_publish_odhcpd_leases.lua; they stub ubus, io.popen and os.execute to validate. The tests run in CI, so regressions in JSON shape or error handling are caught before merging.

c. Testing & Validation

The following testing methods are in progress:

  • Unit tests verify robustness of JSON serialization and shared-state publishing logic, covering normal, empty, and malformed market cases.
  • Deployment tests across three routers:
    • Confirm odhcpd takes over DHCP (uci show dhcp shows maindhcp=1).
    • Check presence and updates of /tmp/ethers.mesh and /etc/ethers symlink.
    • Simulate lease assignment and verify real-time propagation between nodes.
  • Service behavior:
    • Observe leasetrigger invocation on odhcpd-update.
    • Ensure stable operation over lease churn and node restarts.

So, for show how this works, I’ve two routers running LibreMesh, node-1 and node-2.

I connect a device in node-1, then I confirm it with ubus call dhcp ipv4leases '{}'

Then the publisher fires and odhcpd runs the shared-state-publish_odhcpd_leases script, which inserts the JSON blob into the CRDT bus.

Seconds later, on node-2 I dump the CRDT and see the same lease authored by node-1:

This is a minimal example, feel free to test anything you want!

d. Resources

Pull Request of the package,.
Task 2 remains actively in development. Upcoming efforts will involve extensive community testing and careful analysis of how removing dnsmasq‘s DHCP functionality impacts related features and dependencies.

Reflection

The first half of the project required me to dive deeper into LibreMesh’s internals than initially expected, giving me a profound appreciation for this powerful mesh networking tool.

The most valuable lesson was recognizing that removing code (such as the deferrable-reboot script or dnsmasq’s DHCP logic) can be just as rewarding as adding new features. Simplifying the stack enhances its predictability and maintainability, ultimately benefiting the entire LibreMesh community.

Conclussion

After these two initial tasks, LibreMesh now:

  • Reboots through watchcat, an OpenWrt-native tool with LuCI support,
  • Serves and synchronizes DHCP leases via odhcpd and a lightweight CRDT-based sharing mechanism,
  • Incorporates automated tests to ensure reliability and stability through continuous integration (CI).

Looking ahead, I will begin Task 3, removing VLANs from Babel interface, and start prototyping the layer-2-only variant of LibreMesh. I’ll continue employing the methodology proven successful so far: iterative development, backward compatibility, and comprehensive instrumentation.

If future milestones proceed as smoothly, the project will conclude with a cleaner codebase, easier network management, and clearer upgrade paths for community networks.

Leave a Reply

Your email address will not be published. Required fields are marked *