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.

Simplifying LibreMesh with OpenWrt-Native Solutions

Introduction

Hello everyone!
I’m Agustin Trachta, a Computer Engeneering student at National University of Cordoba (Argentina), and I’m thrilled to be participating in Google Summer of Code 2025 with Freifunk, working on LibreMesh!

This summer, I’ll be focusing on simplifying and modernizing LibreMesh by:

  • Migrating legacy components to OpenWrt-native solutions, such as replacing custom scripts like deferrable-reboot with watchcat, and moving DHCP handling from dnsmasq to odhcpd.
  • Removing unnecessary complexity, like VLANs on Babel interfaces, which have caused compatibility and MTU issues.
  • Creating a layer-2-only variant of LibreMesh tailored for lightweight community deployments, based on BATMAN-adv.

My goal is to help make LibreMesh leaner and more accessible to new users, especially communities that rely on low-resource hardware and need plug-and-play reliability. Many of these networks operate in rural or underserved areas where internet access is limited, budgets are tight, and technical expertise is scarce. In such environments, every kilobyte of firmware matters.

By replacing legacy components with OpenWrt-native solutions, we reduce the need for LibreMesh to maintain parallel tools, making the codebase easier to understand and integrate with upstream developments. Additionally, offering a layer-2-only firmware variant allows communities to deploy simpler, lightweight networks that require minimal configuration, consume fewer resources, and are easier to debug.

What I’ll Be Working On

The first task involves replacing LibreMesh’s custom deferrable-reboot script with watchcat, a integrated OpenWrt package. The original script was created to schdeule periodic reboots in order to recover from possible instability after long uptimes, great idea but it’s already implemented in a more robust way, allowing also to trigger network interface restart based on ping failures. Migrating to watchcat ensures better integration with OpenWrt’s LuCI interface.

The second task focuses on improving DHCP handling by transitioning from dnsmasq to odhcpd. While dnsmasq is widely used and remains excelent for DNS forwarding, it’s not ideal for handling modern DHCPv6 configurations and dynamic lease sharing. This migration has been requested for years by the community, and I will work hard on making this a reality!

The third task is about removing VLAN from Babel routing interfaces. LibreMesh always used VLANs to isolate Layer 3 traffic from BATMAN-adv’s Layer 2 mesh, but it introduced issues like lower MTUs, hardware incompatibilities and added configuration burden. My work will include applying and testing relevant patches that have already been worked on, updating configurations and validating that removing VLANs does not introduce routing lops or instability.

Finally, I’ll be developing a Layer-2-only version of LibreMesh, this is other request from the community members who want a simpler and lighter firmware that doesn’t include routing extra daemons. In small mesh networks, users only need a transparent Layer 2 bridge using BATMAN-adv and gateway node doing NAT, my goal is to create a dedicated firmware profile that includes only the essential packages for a Layer-2 mesh and removes unnecessary services. This variant will help networks that value simplicity, speed, and minimal configuration, especially on older routers with tight resource constraints.

Plan of action

To ensure that each change is reliable, compatible, and valuable to the LibreMesh community, I’ll follow a staged and test-driven approach throughout the project.

I’ll begin by setting up a virtualized test environment to quickly prototype and iterate on changes. In parallel, I’ll be using at least three physical routers compatible with LibreMesh to validate the behavior of the firmware under real-world conditions.

Each task will follow a similar cycle:

  1. Development and Integration of the change in the LibreMesh build system.
  2. Configuration and Testing, both in isolated and multi-node environments.
  3. Documentation and Feedback, where I’ll share results with the community, post updates, and adapt based on what users report.

I’ll be actively engaging with the LibreMesh mailing list, GitHub issues, and chat channels to keep the process transparent and collaborative.

Conclusion

I’m very excited to be working on this project as part of the GSoC 2025. I’m looking forward to collaborating, learning, testing, and sharing throughout the summer!

Also I would like to thank my mentor, Javier Jorge, who will be guiding and teaching me a lot about open source projects and local networks.