Neanderfunk 2023.2.6: Packages, Patches, Build System
New Neanderfunk-Stable firmware 26091920sta, still Gluon 2023.2.6, for reasons (see below). What of it lies in our own packages and what remained as patches:
- Feed, pinned to the state in this release: GitHub - Neanderfunk/packages at afccfac4709d5a8627a3592f51cedc847c984f2c · GitHub
- Images, one directory per domain: Freifunk Neanderfunk Firmware Seite
- Firmware, site.conf and release notes, tag
gluon-release-v2023.2.6_26091920sta: GitHub - Neanderfunk/firmware at gluon-release-v2023.2.6_26091920sta · GitHub
In the Images
Selection in templates/common/image-customization.lua, feed pin in templates/common/modules.
- On all devices: nodeplacer, ssid-changer, respondd, setup-mode, setup-wifi, preserve-wifichannel, hotfix, linkcheck, wifi-blackout, weeklyreboot, ap-timer, button-bind, node-whisperer, banner
- Only ramips-mt7621, mediatek-mt7622, mediatek-filogic: mt7915-backlog
- Only as DEPENDS: config-mode-theme, common
- 64 MB RAM, two manually maintained device lists (18 dual-band, 29 single-band): add zram-swap, remove
procd-ujail. Measured on the TL-WR1043ND v2: ujail cost 3.7 MB RSS for dnsmasq, hostapd and ntpd combined. - How the lists are created: candidates from a survey of RAM per device (Gluon targets, OpenWrt Table of Hardware), included is what showed up as affected in the field. The lists are only complete for ath79; single-band devices of other targets with 64 MB are still missing, there it remains at the standard.
- The settings are not tied to these lists, which decide at runtime based on
MemTotal. These apply to every device with 64 MB, including those not on any list; the patches for this are further down. - None of these measures are free, usually throughput pays the price.
- Not included:
haveged(under 23.05, urngd provides entropy, 5.15 initializes CRNG itself; 1.6 MB RSS),ffmuc-ipv6-ra-filter(remains too sticky on fast supernode switchover)
Packages in Detail
For the line Upstream: it almost always means freifunk-gluon/community-packages or the feed of the respective original maintainer, not Gluon-Core. Core is only affected for preserve-wifichannel, setup-wifi and setup-mode.
neanderfunk-preserve-wifichannel
- Function: preserves channel and channel width across updates, resets them after site or domain change
- Mechanics: makes
wifi24.preserve_channelsfrom site.conf the default for Gluon’s uci option - Origin: Gluon 2023.2 does not evaluate this site.conf key. For us it has been set and ineffective for years.
- site.conf:
wifi24.preserve_channels = 1 - Dependency: hiding the outdoor switch in the wizard when
preserve_channels=1still requires a Gluon patch (patches/gluon-config-mode/outdoor-schalter.patch), the package alone is not sufficient. - Upstream: affects Gluon-Core. Gluon knows the uci option
gluon.wireless.preserve_channels, but no site.conf default for it. In progress: Gluon#3550 („RFC: configure wifi through gluon config“, open since 22.07.2025) rebuilds WLAN configuration, Gluon#3601 (open PR) adds an htmode override per radio.
neanderfunk-setup-wifi
- Function: WLAN
setup.gluon_<MAC>in setup mode, setup without network cable - Origin: previously a firmware patch with hardcoded values
- site.conf:
setup_mode.wifiwithstart(button,off,boot),security(open,wpa2),timeout(1200) - Start: via button during operation (
button), on startup into setup mode (boot) or not at all (off). Aftertimeoutseconds the WLAN turns off again. - Remains patch, even when reusing:
S60dnsmasqfromgluon-setup-mode(first instance fixed tobr-setup) andcgi-bin/portalfromgluon-config-mode-core(redirect toSERVER_ADDR). Two packages cannot own the same file. - Upstream: touches Gluon-Core. Without extensibility of these two files, it remains outside a pair of patch and package. Older PR for this: Gluon#1563 („[RFC] gluon-setup-mode: add wifi interfaces“, PR from 04.11.2018, unmerged closed on 05.07.2020). Open point there: a PSK derived from the
primary_macdoes not apply to all devices. Our default key is also derivable from the MAC.
neanderfunk-setup-mode and neanderfunk-config-mode-theme
- Function: setup mode on one page, wizard and advanced settings, one save. Theme responsive, with dark mode.
- Origin: new development.
- DEPENDS:
setup-modeon+neanderfunk-config-mode-theme - Inclusion:
'-gluon-config-mode-theme'is mandatory, both provideview/theme/layout.html. - Why one page: in Gluon’s setup mode, if you switch from the wizard to advanced settings, you lose your input. Without switching no loss. Top the wizard, below each form of advanced settings as a collapsible group with status line, one „Save & Restart“. Information and firmware upgrade remain separate pages.
- The forms remain those from Gluon and the packages. A new package with
model()appears as an additional group without intervention. „Outdoors“ Gluon queries at two places, on the page it appears once. - Storage rules: all or nothing, writing only happens with completely valid forms; only changed forms are written (an empty password form would otherwise have executed
passwd -l root); the wizard last and always, it setsconfigured, callsgluon-reconfigureand restarts; as long as the advanced forms are writing, commit and reconfigure remain out, otherwise a form with an old uci cursor would write a stale copy back. - Theme: replaces Gluon’s theme via
PROVIDES gluon-config-mode-theme, only providesview/theme/layout.htmland a stylesheet and styles Gluon’s own markup without changing any of its templates. No Gluon patch needed. Same pattern asffgraz-config-mode-theme-funkfeuer. - CSS without breakpoints: not a single width media query, the layout is arranged entirely via flex and grid (26 places). The only two
@mediarules areprefers-color-scheme: darkandprefers-reduced-motion: reduce. Dark mode follows the browser, there is no toggle. - Sizes, raw bytes:
neanderfunk.css19,651 B (gzip -9: 5,168 B),layout.html5,142 B. Gluon by comparison:gluon.css7,073 B,layout.html3,194 B. Our stylesheet, however, also covers the collection page, so the comparison is off. - All local: no
@import, nourl(), no@font-face, no external link. Only system fonts (system-ui,-apple-system,Segoe UI, Roboto and further), logo as inline SVG. In setup mode the client usually has no internet, and fonts cost flash. - Cache: the stylesheet is loaded with
?v=<Release>. Gluon serves/static/withoutCache-Controland with fixedLast-Modified, otherwise the browser keeps the old CSS after an update. - i18n: own
i18n/*.po(de, fr), fetched withi18n('neanderfunk-config-mode-theme'), because the dispatcher renders the layout under the namegluon-config-mode-theme. - Upstream: the theme declares
PROVIDES gluon-config-mode-theme, just like the existingffgraz-config-mode-theme-funkfeuerin community-packages; themes as a separate package are thus envisioned there. For the single-page form that touches Gluon-Core, no issue and no PR found. Older issues and PRs: Gluon#2386 („Feature responsive“, PR, unmerged closed on 01.01.2023), Gluon#528 (changeable colors, open since 16.10.2015), Gluon#1165 (status page does not use config-mode-theme, open since 2017).
Setup Mode: Networks, Portal Detection, Button, LED, Timer
- Two networks. On cable
br-setup: node 192.168.1.1, DHCP .2 to .254, router option empty, so the client gets no default route via the node. In setup WLANbr-setupwifi: 198.51.100.1/24, DHCP .2 to .254, router and DNS are the node. - 198.51.100.0/24 is TEST-NET-2 (RFC 5737), deliberately not RFC 1918. Android only opens its login window if the probe host does not resolve to an RFC-1918 address; on Galaxy S24 Ultra with 192.168.2.1 we only got „no internet“.
- DNS on cable targeted, in WLAN catch-all. On cable, dnsmasq only resolves
gluon.setup,setup.gluonand the probe hosts, everything else REFUSED, so a laptop that is also connected to the internet asks its other resolver and gets the OSM tiles in the wizard. In setup WLAN every name points to the node, so the map does not load there. - The probes are manufacturer conventions, not standards. Registered are among others
captive.apple.com,connectivitycheck.gstatic.com,connectivitycheck.android.com,clients3.google.com,www.msftconnecttest.com,dns.msftncsi.com,detectportal.firefox.com,nmcheck.gnome.org,connectivity-check.ubuntu.com,network-test.debian.org,networkcheck.kde.org,ping.archlinux.org,connect.rom.miui.com,connectivitycheck.platform.hicloud.com. - All URLs: uhttpd runs with
-E /cgi-bin/portal, so every path it does not find on the node goes to this handler,/generate_204just as much as/hotspot-detect.html,/connecttest.txtor/success.txt. - The response is the same for all:
302 Found,Location: http://<address>/cgi-bin/config,Cache-Control: no-store(RFC 9110, 15.4.3). Address is the one through which the request came. Because the expected response does not arrive, the system reports „Sign in to network“ and opens the wizard. - Not implemented: standardized signaling per RFC 8910 (DHCPv4 option 114, DHCPv6 option 103, RA option 37) with Captive Portal API per RFC 8908. The API would need HTTPS with a valid certificate, otherwise the client must ignore it, and a node in setup mode does not have one: it is offline until configured, and in this state also cannot obtain one. Add to that iOS not using the API on first connection and DHCPv6 as well as RA falling away because Gluon’s dnsmasq is built without both and setup mode provides no IPv6. RFC 6585 (HTTP 511) also not, we respond with 302.
- Tested is Android, Galaxy S24 Ultra and a device with Android 16 on 14.09.2026, plus lab runs on Archer C25 v1. iOS, macOS, Windows, the Linux desktops and the Xiaomi and Huawei ROMs are registered, their effect is not verified.
- Button: Gluon itself starts on 3 s hold on reset, wps or phone into setup mode. Our package takes the short press under 3 s and only in setup mode: turn on setup WLAN, or the timeout starts over. A factory reset by button does not exist in the image,
/etc/rc.button/resetis missing. - LED: setup mode 1000 ms on, 300 ms off, so about 0.77 Hz. If setup WLAN is running, 200/60 ms, so about 3.8 Hz. Until 14.09.2026 it was 333/100, that was too hard to distinguish from the setup mode beat.
- Timer: default 1200 s, allowed 60 to 86400. Only extended by another short press, client traffic does not extend it. After that
wifi downand a log line, no restart; access via cable remains. - For us:
start = button,security = open. So the setup WLAN is open, but only goes on via short press on the device.check_site.luaforces this combination, open only exists together withbutton. On Genexis Pulse EX400 the capacitive wps area counts for the short press, there the open setup WLAN can go on without fingers.
neanderfunk-hotfix
- Function: 16 state checks that reinitialize WLAN or reboot. Intervals: healthcheck 7 min, IfNoWificlient 15 min, watchdog 5 min, WLAN firmware 3 min.
- Checks and reaction:
kernel_bug,ath_malloc,ksoftirqd_malloc,tunneldigger,br_client_ipv6,load,respondd,dropbear,wifi_firmware,ath10k_rxhang,eth_tx_stall,watchdog(reboot);hostapd_pids,dfs_failcheck,no_wifi_clients(WLAN restart);logremote(restart the logread instance) - uci:
hotfix.settings.check_uptime_min(5),.reboot_uptime_min(60),.watchdog_interval_min(5),.autoupdater_stale_min(300),.hostapd_cooldown_min(60),.ath10k_restart_min(3); per checkhotfix.<check>.disabledand.immediate;hotfix.load.per_cpu(2, threshold is per_cpu times cores over two runs);hotfix.eth_tx_stall.dry_run(1 = log only),.strikes(3),.rx_strikes(5),.soft_reset(1,ethtool -rbefore reboot) - site.conf:
hotfix.check_uptime_min,.reboot_uptime_min,.disabled_checks,.load_per_cpu,.eth_tx_stall_dry_run - Origin:
eulenfunk/packages(Ruben Kelevra), contains and replacesffac-mt7915-hotfix. New compared to the predecessor are both uptime thresholds; the original could reboot two minutes after boot. - Upstream: contains and replaces
ffac-mt7915-hotfix(community-packages#132, 2024). The causes lie deeper:eth_tx_stallbelongs to openwrt/openwrt#17505 („Cudy TR3000: MT7981 ethernet disconnects“, open, last 07.01.2025),kernel_bugstems from the time of Gluon#680 (closed). - DEPENDS:
+neanderfunk-common
neanderfunk-linkcheck
- Function: checks WLAN and batman neighbors as well as gateway reachability. Intervals: linkcheck 5 min, gateway 8 min.
- Checks:
batadv_neighbours,bsses,batinterfaces,batman_originators,bridges,bridge_ports,mesh_neighbours(first WLAN restart, then reboot);no_gateway,ipv6_anycast,public_prefix(reboot) - uci:
linkcheck.settings.check_uptime_min(5),.reboot_uptime_min(60), per checklinkcheck.<check>.disabled - site.conf:
linkcheck.check_uptime_min,.reboot_uptime_min,.disabled_checks - Limits:
no_gatewayreboots after 4 runs, so about 32 min, but only if a gateway was seen once since boot (marker/tmp/linkcheck.gw-seen). With flickering gateway results in a reboot loop of about 65 min;reboot_uptime_mindelays it, it does not prevent it. A node that never had a gateway stays at rest.bssesskips wifi6 and mt7915 radios, scanning breaks mesh links there.
- Origin:
eulenfunk/packages(Ruben Kelevra). - DEPENDS:
+neanderfunk-common
neanderfunk-wifi-blackout
- Function: no associated client on any radio for
blackoutwaitminutes, then WLAN restart; if that does not help, reboot. - uci:
wifi_blackout.settings.check_uptime_min(5),.restarts_before_reboot(1),.disabled(0) - site.conf, not via uci:
wifi_blackout.blackoutwait(171 min),.resetwait(281 min),.stepsize(10 min) - False positive: a node where clients legitimately never hang, i.e., a pure mesh or backbone node, periodically restarts. Measured on D-Link COVR-X1860 without clients: about every 14 h.
- Origin: FreifunkHochstift, via
eulenfunk-ath9kblackout. Renamed because the issue is not ath9k-specific. - DEPENDS:
+neanderfunk-common - CONFLICTS:
eulenfunk-ath9kblackout,gluon-ath9kblackout,neanderfunk-ath9kblackout
neanderfunk-mt7915-backlog
- Function: WLAN restart if the txq backlog of an mt7915 radio grows over 50 packets. Only on ramips-mt7621, mediatek-mt7622, mediatek-filogic.
- uci:
mt7915backlog.settings.threshold(50),.cooldown_min(30),.disabled(0),.check_uptime_min(5),.reboot_uptime_min(60) - Origin: port of
ffac-mt7915-backlog. New is 30 min cooldown between restarts, the original restarted up to every 2 min. - Classification: symptom treatment, for us together with
ffac-mt7915-maxinactivity, which addresses the cause. - Upstream:
ffac-mt7915-backlogonly exists as a branch in ffac/gluon-packages, PR #19 from 30.09.2025 was unmerged closed. The cause is open as openwrt/mt76#1009 („mt7915: wifi unresponsive - backlog fills - no tx activity“, last 22.04.2026). Gluon#3154 (MT7915e crashes) closed since 15.04.2025, fixed by Gluon#3436 („mt76: fix system recovery routine for MT7915“, merged 09.03.2025), but only in main. The backport Gluon#3710 for v2023.2.x was not merged, reasoning in the PR: „This issue is properly fixed in future Gluon releases“. On 2023.2.6 the fix is thus missing.
neanderfunk-ssid-changer
- Function: switches the client SSID to an offline SSID when no gateway is reachable, and back when it is available again.
- uci:
ssid-changer.settings.enabled,.switch_timeframe(30 min),.first(5 min),.prefix(FF_Offline_),.prefix_owe(FF_Off_OWE_),.suffix(nodename,mac,none),.tq_limit_enabled(false),.tq_limit_max(45),.tq_limit_min(35),.gwofflinemaxcount(3),.debug_log_enabled - site.conf: block
ssid_changer - Limitation: the TQ limits do not work with BATMAN_V per README.
- Counters are not in uci, but as respondd values under
statistics.neanderfunk.ssid_changer.{gateway_losses, offline, switches}. - Origin: fork of
ffac-ssid-changer(base commit91e5fa8). - DEPENDS:
+neanderfunk-common - CONFLICTS:
ffac-ssid-changer,gluon-ssid-changer - Upstream: at the original open: community-packages#180 („set offline ssid correctly for larger switch_timeframe“, open PR from 08.01.2026), #172 and #173 (offloader without WLAN, 09/2025), #157 („Never sets offline SSID“, closed).
neanderfunk-nodeplacer
- Function: server-side moves individual nodes to a different domain, controlled via a signed manifest on the firmware server.
- site.conf, all optional, the block itself too: installed means active.
nodeplacer.mirrors(onlyhttp://),.pubkeys,.good_signatures(at most number of pubkeys),.disable(0),.config_mode(tab in setup mode). Without specification, mirrors, keys and threshold of the autoupdater branch on which the node is running apply. - uci:
nodeplacer.settings.* - Trust anchor: same keys and same threshold as the autoupdater.
- Limitations: only within the same community; tested with single-domain firmware, multidomain not yet on devices.
- CONFLICTS:
gluon-hoodselector - Documentation:
neanderfunk-nodeplacer/docs/HOWTO.mdanddocs/MANIFEST-FORMAT.md - Upstream: new development. Related, but different purpose:
gluon-scheduled-domain-switch(whole domain, time-controlled),gluon-hoodselector(by geo coordinates),gluon-config-mode-domain-select(selection by operator). None of those cover individual nodes server-side. README anddocs/DECISIONS.mdlie in the package, the README requests testing by other communities: packages/neanderfunk-nodeplacer at afccfac4709d5a8627a3592f51cedc847c984f2c · Neanderfunk/packages · GitHub
neanderfunk-respondd
- Function: respondd module in C, provides
statistics.neanderfunk.*: hardware, radios (channel, htmode, txpower, country), offline SSID counters, ethernet links (carrier, speed, possible), memory pressure (refault_file, forks, zram), temperatures. - Configuration: none, neither uci nor site.conf.
- For the values to appear on a map, yanic needs an extension. Ours is publicly available as a commit in our yanic fork: statistics.neanderfunk lesen und in die Zeitreihen schreiben · Neanderfunk/yanic@20c5b02 · GitHub
- Not all of it lands there: hardware information (
vendor,soc,cpu_model,flash) yanic does not read, they are only on the node. - Upstream: new development. Field names are deliberately under
statistics.neanderfunk.*, i.e., as community extension not as Gluon standard field.
neanderfunk-ap-timer
- Function: time switch for the client WLAN, with page in setup mode.
- uci:
ap-timer.settings.enabled,.type(dayorweek), then per day or weeklist on 'HH:MM'andlist off 'HH:MM' - site.conf:
ap_timer.web(page in setup mode, default true) - Origin: fork of
ff-ap-timerandff-web-ap-timer, combined into one package (community-packages#80, 2023, originally from FFHO). - CONFLICTS:
ff-ap-timer,ff-web-ap-timer
neanderfunk-button-bind
- Function: assigns functions to device buttons.
- uci:
button-bind.wifi.function: 0 = WLAN on/off (OpenWrt behavior), 1 = no function (default), 2 = WLAN reset, 3 = night mode (LEDs off, only on press, requires reboot) - Origin: port of
ffffm-button-bind(GitHub - freifunk-ffm/packages: Frankfurter Packages · GitHub), with bugfix in upgrade script. - CONFLICTS:
ffffm-button-bind - Upstream: the original repo is not archived, last push 10.07.2024.
neanderfunk-node-whisperer
- Function: diagnostic information via WLAN beacons.
- site.conf:
node_whisperer.enabled,.information(selection from hostname, node_id, uptime, site_code, domain, system_load, firmware_version, batman_adv) - Origin: fork of
ffda-node-whisperer. Our changes lie as patches in the package: return-ENODATAinstead of incorrect values, gateway TQ viaBATADV_CMD_GET_GATEWAYS, each originator counted once. - CONFLICTS:
ffda-node-whisperer - Upstream: community-packages#182 (open since 03.04.2026) reports literally
node-whisperer: Error collecting Information for id=4 name=domain code=-1. Our patch 0001 addresses exactly this message:gluonutil_get_domain()returns NULL on single-domain firmware, the collector returned -1 for that. In the issue the cause is suspected to be incomplete WLAN detection.
neanderfunk-banner
- Function: replaces
/etc/bannerand/etc/profilewith variants showing node status and helper commands. - Note: makes DNS queries to
asn.cymru.comfor ASN display. - CONFLICTS:
ffmuc-custom-banner(community-packages#115, 2024).
neanderfunk-common
- Function: shared helpers for hotfix, linkcheck, weeklyreboot, ssid-changer and wifi-blackout, including the reboot log
/lib/gluon/neanderfunk/reboot.logwhich preserves the last reboot reasons across restarts. - uci:
neanderfunk.settings.reboot_log_max(6, 0 = off) - site.conf:
neanderfunk.reboot_log_max
neanderfunk-weeklyreboot
- Function: reboot Thursday,
cron 15 3 * * 4plus 0 to 9999 s randomness, so between 3:15 and about 6:02. - Skipped while autoupdater is working, two-stage:
flock -non/var/lock/autoupdater.lockcovers download and check,pgrep sysupgradethe flash itself, both fail-safe. - Without set clock the reboot depends on uptime instead of day of week. Most routers have no RTC. After boot OpenWrt sets the clock to the timestamp of the newest file under
/etc, so about the last configuration change. If that was shortly before Thursday 3:15, the node reaches the cron time after every boot after the same short period, and earlier versions restarted it that way every time. The script therefore checks a marker in/tmpwhich the ntp hotplug eventstratumsets. Without it, the Thursday run only reboots from seven days uptime, the restart then falls between 7 and 14 days. - Configuration: none. The schedule is fixed; for other times the cron line must be changed and rebuilt.
- CONFLICTS:
ffac-weeklyreboot,gluon-weeklyreboot - DEPENDS:
+neanderfunk-common - Upstream: counterpart is
ffac-weeklyreboot(community-packages#34, 2023). It checked/tmp/autoupdate.lock, a file that nobody creates. Corrected in community-packages#191, upstream merged on 10.09.2026, now alsoflock -non/var/lock/autoupdater.lock.flockis present on 2023.2.6 images (busybox 1.36.1, checked on x86 and TL-WR1043ND v2). The second stagepgrep sysupgradeand the ntp protection are not upstream.
What Remained as Patch and Why
40 patches, fixed order in templates/common/prepare.sh, grouped under patches/<group>/. Each group is self-contained, the scripts are idempotent; patches/README.md describes how to take individual groups.
Backports: upstream solved, not yet in 2023.2
lowmem/limit-wireless-buffers- WLAN buffers based on RAM. Gluon8f38662f, from v2025.1.lowmem/sysctl-64m-min-free-min_free_kbytes2048, small fragment buffers. Gluona505f767plus fixc6ac8914, only in main, in no release.status-page/statuspage-ssid- SSID and HT mode per radio. Gluon7c040c2d04ee, from v2025.1, for us with two deliberate deviations.kernel/rtl8221b-skip-mmd30- skip MMD 30 on C45 PHY scan, otherwise reading RTL8221B hangs if a cable is connected at boot. OpenWrt88dcd8c303b6, only in main, neither 24.10 nor 23.05.devices/add-dlink-m30- in Gluon v2025.1.3 and main, not in v2023.2.x. OpenWrt 23.056e51ff88b0plus fix from 24.10d92fc99360. Without recovery image, as Gluon itself since freifunk-gluon/gluon#3816.
TLB Kernel Freeze, Upstream Regression in Gluon 2023.2.6 Release, Resolved
kernel/revert-mips-tlb-uniquifyremoves the call tor4k_tlb_uniquify(). Affected MIPS boards otherwise hang on cold start intlb_init(), but survive warm starts and updates.- This is the reason for the „for reasons“ above. This bug failed a stable for us in March: rolled out on 23.03.2026, rolled back on 27.03.2026. Devices continued running after the update, the bug only showed on the next power outage, and then the device was gone. This could neither show up during rollout nor in the update statistics.
- Rolled back via manifest: a pre-dated, newly signed manifest with the old version on its own branch. The autoupdater takes an older firmware if the manifest names it as the current one.
- Upstream solved it differently: 5.15.209 rewrites uniquification (
540760b77b8f,Fixes: 9f048fa48740). For routers no functional difference. - openwrt-23.05 has 5.15.211 since 11.07.2026, Gluon v2023.2.x since 22.09.2026 (freifunk-gluon/gluon#3841).
- Our patch will necessarily be dropped on the next build, it applies to code that 5.15.209 rewrote.
- Analysis and measurement public: freifunk-docs/mips-tlb-cold-start-5.15.md at 15fcc7bc579df8d400b12c3650a87fbf45994ea7 · Neanderfunk/freifunk-docs · GitHub
Depends on Open Issues Upstream
kernel/mt7530-phy-disable-eee- disable EEE on MT7530 PHY. Rebuild of OpenWrt PR #25058 (open, base main), transferred to 5.15 by us. Unverified: built, the effect on the device was not specifically measured.build/add-ffac-package-patches-max_inactivityin ffac package, workaround for openwrt/mt76#1009 (open): mt7915 becomes unresponsive, backlog fills.
Issues and Gaps without Bug Report Upstream
kernel/ag71xx-rx-ring-no-bug- under memory pressure filling the RX ring fails,ag71xx_assert(0)triggers a panic, even though the driver has a fallback withoom_timer. OpenWrt main has that section unchanged (ag71xx_main.c,ag71xx_rx_packets).bugfixes/tunneldigger-reinit-backoff- mesh VPN on, no WAN: reinit without pause, twomodprobeper reinit. Measured on WDR3600: 115modprobein 30 s, CPU 0% idle.lowmem/sysctl-no-watermark-boost-64mbandbugfixes/sysctl-firmware-no-sysfs-fallback(missing firmware liketg3held boot 60 s under RTNL). Both could also be solved as a package withsysctl.dfile.gluon-config-mode/wizard-save-lock- double-click on „Save & Restart“ starts parallelgluon-reconfigureruns, result hostnameOpenWrtand timezone UTC.status-page/web-static-version- CSS and JS with?v=<Release>against old browser cache.- Airtime part of
build/add-gluon-package-patches-respondd-module-airtimeomits values wherebusy,rxortxis greater thanactive; the mt76 survey counters run per vif.
Principally Not a Package
lowmem/state-check-shell,lowmem/tunneldigger-watchdog-shell-gluon-state-checkandtunneldigger-watchdogas shell instead of Lua, because the Lua runtime on 64 MB devices came from flash every minute respectively every five minutes. Not solvable as a package: they replace files owned by Gluon packages, that would cause an opkg conflict.status-page/*- Gluon’s status page has no extension point in 2023.2. The data now comes fromneanderfunk-respondd, as a patch the display remains.setup-mode-network/*- skeleton of 14 lines: the binding ofdnsmasqtobr-setupand the portal redirect. The restneanderfunk-setup-wifihas taken over.- Devices that Gluon 2025.1 also does not have: eap225-wall-v2, zyxel-nbg6616, fritz-repeater-3000, ea8300-dallas, cudy-m1800, rb750gr3, Cudy ap3000-v1 and tr3000-256mb-v1, FRITZ!Box 3390, two MikroTik ath79, zte-mf286r, MikroTik wAP ac, NanoPi R2C.
device-fixes/fix-xiaomi-ax6s-bootflags- the A/B bootloader of Redmi AX6S otherwise falls back to stock firmware after a few reboots. Verified on device; whether OpenWrt now solves it differently is not checked.- Local only:
network/interface-role-migration21(our 2021 migration),build/patch-gluon-makefiles(GLUON_TARGETSvia override).
What Sits Next to the Images
In each domain directory next to ./sysupgrade, ./factory and ./other is a directory ./site describing the build. Whoever wants can thus repeat the build. It is not a reproducible build, it is about transparency:
- Gluon standard, handled in one line:
site.conf,site.mk,modules,image-customization.luaandi18n/are what every Gluon site has. build-info.txt- the origin of the image: release, domain, template, branch, start and end of run, buildhost and call, full commit IDs of firmware repo and Gluon with note clean or N changes, per module desired commit and current state, target and domain list, kernel and WLAN driver versions from the OpenWrt manifest and each applied patch with result.build.conf- the run parameters ofbuild.sh, commented: version scheme,make cleanandgit reset,-j, BROKEN, autoupdater, log options, name of signing key file, worker count and space check. Which targets and domains to build is not here.targets.conf- which Gluon targets to build. Deselected ones are prefixed with-, each with node count commented.domains.conf- which domains in which order to build.build.sh- the build system itself: reset Gluon tree, call prepare, golden tree and worker overlays, build loop over targets and domains, sign manifest, place result with this directory.prepare.sh- applies patches frompatches/in fixed order, in two phases before and after Gluon’smake update.prepare.log- the complete log of preparation, once per run and same for all domains: reset,make updatewith Gluon’s patches, our patches, feeds and downloads. Without the compiling.build.log.gz- the completemakelog withV=sfor this one domain over all 20 targets, about 367,000 lines. Note: the first built domain compiles everything, in each further one almost only image assembly remains.
How It Is Built
Measurement report (in English) with raw data and analysis script, including the release run: firmware/docs/parallel-builds/README.md at de00b9dba382a347eab20bbc783fc7b1fd1627ff · Neanderfunk/firmware · GitHub
- Golden tree. The Gluon tree is fully built once, serially with full
-j:git reset,make clean,make update, our patches, then the first domain for all targets. After that it is read-only. It contains everything OpenWrt builds, toolchain, packages, kernel,build_dirandstaging_dirper target. - Reused via a fingerprint of inputs: Gluon commit, target list, device selection, BROKEN, gcc and libc version of the host, SHA-256 of all patches and common template files. If it matches, prepare and golden tree are skipped entirely. Patches are then not even re-applied, because new mtimes would cause OpenWrt to rebuild.
- Why it helps at all: a domain change compiles no single package, only
package/gluon-sitehangs on the site directory. Costs lie per image, not per config, measured about 94 min per domain for 448 images, so about 12.6 s per image. - Workers on kernel overlayfs, not fuse-overlayfs: lower is the golden tree, held via bind mount, upper a private directory per worker. Mounted at the original tree path because OpenWrt writes absolute paths into its
.preparedstamps. - Rootless: two user namespaces (
unshare -Urm), overlay withuserxattr, then back to normal UID, because OpenWrt does not build as root. Prerequisites: Linux from 5.11, util-linux from 2.38, bash from 5.1, on Ubuntu from 23.10 additionallykernel.apparmor_restrict_unprivileged_userns=0.build.shpre-tries a real overlay mount and continues serially if it fails. - Split by targets, not domains. A worker builds one target for all domains, then its upper is deleted. Reason is space: the overlay grows per target (first domain 2.8 GB, each further 0.2 GB), on the target axis about 11 GB per worker, on the domain axis it would be about 62 GB.
- Cost of the overlay, measured: mount 0.00 s, discard about 3 s for 3 GB and 70,000 files. Overhead with all devices from ramips-mt7621 was unmeasurable (241/219 s direct vs. 238/224 s in overlay); with only two devices, where fixed costs dominate, 2.6%.
- What parallelism brings, measured: subsequent domains ran with six workers 2.7 to 2.8 times faster than serially, not six times. Each individual step takes about 1.7 times as long under load, with nine workers about 1.85 times. The cause is narrowed down but not proven, most likely the clock under full load.
- The release run: 86 variants times 20 targets, 1720 steps, 8 workers in three waves, 37,066 images totaling 325.7 GB. From zero about 40 h, distributed over two runs because the first was halted after 14 of 20 golden steps. The second run took 36 h 29 min: prepare 82 min, remaining golden steps 136 min, parallel phase 32.4 h at an average 6.8 of 8 workers used, wrap-up 29 min. A serial comparison run of this scope does not exist. Extrapolated from measured individual costs it would be about 138 h, calculated for 22 targets.
- Limits:
- fuse-overlayfs is unsuitable. OpenWrt does
rm -rf root.orig-<target>and immediately aftercp -fpR; there the lower directory reappears andcpfails with „File exists“. Kernel overlayfs sets the whiteout correctly. - No overlayfs on overlayfs. In the container
/tmpis not suitable as upper, a bind-mounted ext4 path is. - The golden tree itself is serial and with few domains the largest item. Parallelizing it is hard because all targets build into the same tree.
- The parallel phase is never shorter than the longest target, for us ath79-generic, and the last wave runs with few workers.
- Occupied on a host with few runs. Only one config was repeated, deviation about 1%. x86-64 was not part of the overlay measurements.
- fuse-overlayfs is unsuitable. OpenWrt does
If You Want to Reuse Something from This
The packages go individually, the build system less so.
- Feed in
site/modules, pinned to the state from this release:PACKAGES_NEANDERFUNK_REPO=https://github.com/Neanderfunk/packages.git,_BRANCH=v2023.2.x,_COMMIT=afccfac4709d5a8627a3592f51cedc847c984f2c. Built for Gluon v2023.2.x. - Package names in
image-customization.lua.neanderfunk-setup-modebrings its own theme, without'-gluon-config-mode-theme'the build breaks. - CONFLICTS are listed per package above, the original must come out of the package list. Exception:
hotfixandmt7915-backlogin the release state declare no CONFLICTS againstffac-mt7915-hotfixandffac-mt7915-backlog. If you have both, you get two watchdogs running in parallel, without error message. Remove the ffac packages by hand. - Patches:
patches/README.mdin the tag, section „Take individual patches“. Copy group,patches/lib-patch.shwith it, call script from the Gluon directory, mind the phase. - Do not take:
kernel/revert-mips-tlb-uniquify(in v2023.2.x superfluous since 22.09.2026),lowmem/limit-wireless-buffers(dropped with 2025.1),network/interface-role-migration21(our 2021 migration) and everything underbuild/(our opkg keys). build.sh,targets.conf, domains and site templates are tuned to us and not built to run elsewhere.
Questions welcome here in the thread.