Skip to content

alfred: add new package - #30161

Open
BKPepe wants to merge 2 commits into
openwrt:masterfrom
BKPepe:add-alfred
Open

alfred: add new package#30161
BKPepe wants to merge 2 commits into
openwrt:masterfrom
BKPepe:add-alfred

Conversation

@BKPepe

@BKPepe BKPepe commented Aug 6, 2026

Copy link
Copy Markdown
Member

Adds alfred from the openwrt/routing feed — the routing packages are being moved into openwrt/packages one by one, as discussed in openwrt/routing#184.

Includes the Makefile cleanup pending in openwrt/routing#1192.

The content matches the current routing feed master. Once this is merged, the package will be removed from the routing feed (a coordinated removal PR is prepared there).

Maintainer: @simonwunderlich

Copilot AI lite review requested due to automatic review settings August 6, 2026 08:09
@openwrt openwrt Bot added the Add package label Aug 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ecsv

ecsv commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@BKPepe I think we had that before. @simonwunderlich is the maintainer - as stated in the Makefile

@BKPepe

BKPepe commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Right, sorry — fixed the PR description, @simonwunderlich is the maintainer as stated in the Makefile. Same for batctl (#30163) and batman-adv (#30164).

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Makefile cleanup looks right: the define blocks use the indentation each kind actually needs (two spaces for the metadata block, tabs for install and config, none for conffiles), PKG_RELEASE:=1 is correct for a new package, PKG_MAINTAINER is set, and BuildPackage is called with the literal name rather than $(PKG_NAME).

Everything I flagged is a carry-over from the routing feed rather than something this PR introduces, so none of it blocks the move. The two worth a look — before or in a follow-up — are the loadstring on mesh-sourced data and the unchecked io.open in bat-hosts.lua; the second one is a plain crash on any node with the bonding kmod loaded. The rest are marked nit:.

Two things I could not check from here, flagging so nobody assumes they were verified: PKG_HASH and the existence of the 2026.2 tarball (downloads.open-mesh.org is not reachable in this environment), and byte-for-byte parity with the current routing-feed master (no access to openwrt/routing). CI on the head commit is still pending, so there is nothing to read from it yet.


Generated by Claude Code

local output = fd:read("*a")
fd:close()
if output and output ~= "" then
assert(loadstring("rows = {" .. output .. "}"))()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadstring executes the output of alfred -r 64 as Lua code, and that output is assembled from payloads published by every other node on the mesh. If a remote node can get a " or a \ through alfred's escaping into a type-64 payload, it breaks out of the string literal and runs arbitrary Lua as root on every node with PACKAGE_ALFRED_BATHOSTS enabled — and this script is re-run from cron every 5 minutes, see the crontab line installed at alfred.init:81.

alfred's client does escape quotes/backslashes/non-printables as \xNN when it prints the { "mac", "data" } records, so this is probably safe in practice — but it makes a remote-input trust boundary depend entirely on the exact escaping of another program, with no local check. Parsing the records with a string.gmatch pattern (the MAC is already validated with ^%x%x:... at line 44) instead of evaluating them would remove the question entirely.

Is relying on alfred's escaping here deliberate?


Generated by Claude Code

Comment on lines +22 to +27
function get_interface_address(name)
local addressfile = io.open("/sys/class/net/"..name.."/address", "r")
local ret_string = addressfile:read()
addressfile:close()
return ret_string
end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

io.open is not checked before addressfile:read() is called on it. /sys/class/net/ does not contain only interfaces: with the bonding kmod loaded it also contains the plain file bonding_masters, which the ls -1 at line 15 happily returns and which has no address below it. io.open then yields nil, nil:read() raises "attempt to index a nil value", and the whole facter dies — so no bat-hosts data gets published at all on such a node.

Suggested change
function get_interface_address(name)
local addressfile = io.open("/sys/class/net/"..name.."/address", "r")
local ret_string = addressfile:read()
addressfile:close()
return ret_string
end
function get_interface_address(name)
local addressfile = io.open("/sys/class/net/"..name.."/address", "r")
if not addressfile then return nil end
local ret_string = addressfile:read()
addressfile:close()
return ret_string
end

Returning nil needs a matching guard at the caller: line 40 would otherwise do ifaces[nil] = i, which raises "table index is nil". if address and not ifaces[address] then ifaces[address] = i end covers it.

get_hostname at line 6 has the same unchecked-io.open shape, though /proc/sys/kernel/hostname is always there in practice.


Generated by Claude Code

config_get batmanif "$section" batmanif
append args "-b $batmanif"

append alfred_args "$args"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config_foreach runs alfred_start for every alfred section, but the results are accumulated into one flat alfred_args that feeds a single procd instance. Two enabled sections therefore produce alfred -f -i eth0 -m -b bat0 -f -i eth1 -b bat1 — one daemon with repeated -i/-b, not two daemons. vis_args at line 52 has the same shape, and run_facters ends up holding whatever the last section set.

Is a single section the only supported configuration? If so, opening one procd instance per section inside alfred_start (instance name derived from $section) would make the multi-section case actually work, or the config_foreach could go away in favour of a single named section so the limitation is visible in the script.


Generated by Claude Code

alfred_start() {
local args=""
local section="$1"
local disabled interface mode

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: batmanif and start_vis are used only inside alfred_start (lines 43-52) but are not declared local, unlike their neighbours on this line, so they leak into the rc.common shell and carry over between sections.

Suggested change
local disabled interface mode
local disabled interface mode batmanif start_vis

run_facters has to stay global — start_service reads it at line 78.


Generated by Claude Code

Comment on lines +7 to +8
# REMOVE THIS LINE TO ENABLE ALFRED
option disabled '1'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "THIS LINE" reads as the comment itself, but deleting the comment does nothing — it's the option disabled '1' below that has to go.

Suggested change
# REMOVE THIS LINE TO ENABLE ALFRED
option disabled '1'
# REMOVE THE LINE BELOW TO ENABLE ALFRED
option disabled '1'

Generated by Claude Code

Comment thread net/alfred/Makefile

define Package/alfred/conffiles
/etc/config/alfred
/etc/alfred/bat-hosts.lua

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this entry is unconditional, but the file is only installed when CONFIG_PACKAGE_ALFRED_BATHOSTS is set (lines 84-85), and it goes in via $(INSTALL_BIN) as an executable facter script rather than as a config file.

Nothing breaks — the packaging step skips conffiles that aren't in the image (package-pack.mk: [ -f $(IDIR)/$file ] || continue) — so this is only worth confirming the entry is intentional rather than a leftover.


Generated by Claude Code

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the one new commit since 0ef968b (alfred: handle unreadable files in bat-hosts.lua). The commit message matches what the diff does, and the get_interface_address path is now correct — nil is returned and the caller skips those entries, so a node with the bonding kmod loaded no longer kills the facter.

One loose end: the get_hostname guard has no caller-side counterpart, so it trades "attempt to index a nil value" for "attempt to concatenate a nil value" a few lines later. Inline comment with a suggestion. Not a blocker — /proc/sys/kernel/hostname is always present in practice, same as before this commit.

The earlier threads on loadstring, the init script's single-instance config_foreach, and the nit: items are untouched by this commit and still open; nothing to add there.

CI on 2bb2c4d is green across all ten build targets and all three FormalityCheck jobs. Still not verified from here: PKG_HASH / the 2026.2 tarball (downloads.open-mesh.org unreachable in this environment) and byte-for-byte parity with the current routing-feed master.


Generated by Claude Code

local n, i
local ifaces, ret = {}, {}

local hostname = get_hostname()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get_interface_address half of this fix is complete — nil is returned and the caller now skips it. The get_hostname half is not: the new if not hostfile then return nil end at line 7 has no matching guard here, so hostname can be nil and mac.." "..hostname.."_"..iname at line 49 raises "attempt to concatenate a nil value". The guard converts one crash into a different crash rather than removing it.

Same applies when the file opens but is empty — hostfile:read() returns nil too, which the if not hostfile check does not cover.

Suggested change
local hostname = get_hostname()
local hostname = get_hostname()
if not hostname then return ret end

Bailing out with the empty ret means publish_bat_hosts writes an empty chunk instead of dying, which matches how the rest of the script degrades.


Generated by Claude Code

BKPepe added a commit to BKPepe/openwrt-routing that referenced this pull request Aug 11, 2026
The package has been moved to the openwrt/packages feed, as
discussed in openwrt#184.

See openwrt/packages#30161.

Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BKPepe and others added 2 commits August 11, 2026 22:02
alfred is a user space daemon for distributing arbitrary local
information (e.g. hostnames or vis data) over a batman-adv mesh
network in a decentralized fashion via IPv6 link-local multicast.

Moved from the openwrt/routing feed, as discussed in
openwrt/routing#184.

Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
get_interface_address() calls io.open() and dereferences the result
without checking it. /sys/class/net does not contain interfaces
only: with the bonding module loaded it also holds the plain file
bonding_masters, which the `ls -1` in get_interfaces_names() returns
and which has no address below it. io.open() then returns nil and
the script dies with "attempt to index a nil value", so no bat-hosts
data is published at all on such a node.

Return nil when the file cannot be opened and skip those entries in
the caller, which would otherwise index the interface table with
nil. get_hostname() gets the same guard.

Reported-by: openwrt-ai[bot]
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants