The initial modem import used a standalone infix-modem YANG module with
an integer-indexed list under /infix-modem:modems carrying a bearer
config mixed in with hardware config. Infix has adopted the ietf-hardware
model to allow for a clearer separation of concerns, this commit aims to
improve on that situation by refactoring the modem model to use the same
architectural pattern already used for WiFi radios, GPS receivers, and
USB ports:
Hardware:
/ietf-hardware:hardware/component[class=infix-hardware:modem]
/ietf-hardware:hardware/component[class=infix-hardware:sim]
Interface:
/ietf-interfaces:interfaces/interface[type=infix-if-type:modem]
Configuration split:
modem component — admin-state (enable/disable), radio access mode,
frequency bands, location services, probe-timeout
sim component — PIN, PUK, carrier profile
wwan interface — APN, IP type, roaming, route-preference, auth
This also enables the modem to appear in 'show hardware' alongside
other hardware components, and to be managed over NETCONF/RESTCONF
using the same ietf-hardware RPCs used for USB and WiFi.
YANG changes:
- infix-modem.yang removed (1089 lines, standalone module)
- infix-hardware.yang: new modem and sim containers, modem-state and
sim-state operational state, typedefs for bands/modes/location,
notification status-update; actions restart/reset/send-sms are
augmented directly on the component list entry (not inside container
modem) to work around sysrepo NP-container parent validation — when
checking a nested action's parent, sysrepo restricts its operational
data fetch to the container path, excluding the sibling 'class' leaf
needed to evaluate the container's 'when' condition, so the container
is never instantiated and the parent check fails
- infix-if-modem.yang: new submodule (same pattern as wifi/wireguard)
adds the wwan bearer container to ietf-interfaces
- infix-if-type.yang: new modem identity for wwan interface type
- confd version bumped to 1.9
confd:
- if-modem.c: new file replaces the stub modem_gen() in modem.c;
generates dagger init scripts with probe-timeout wait loop and
unconditional dummy wwan creation so downstream IP config succeeds
when a USB modem is slow to enumerate at boot
- hardware.c: start/stop/enable/disable modemd (and modem-manager)
in response to admin-state changes on the modem hardware component;
use finit_enable/disable consistently
- hardware_cand_infer_class(): auto-set class for modemN/simN names
- modem.c: stripped to RPC/notification forwarding only; the old
genconf() / enable() / disable() logic is gone — hardware.c owns
the lifecycle now
- interfaces.c: remove wwan from wait-interface timeout (the modem
interface appearance is managed by modemd, not confd's init scripts)
- migrate/1.9: script fully migrates /infix-modem:modems to the new
split model; each modem[N] becomes a modemN hardware component and
simN sim component; each bearer becomes a wwanN interface (bearers
numbered globally across modems); APN, IP type, roaming, preferred-
mode, bands, location, PIN/PUK/carrier are all carried over; plain-
text bearer credentials are moved to a named keystore symmetric-key
(base64-encoded) referenced from bearer/authentication/password;
apn-type, firewall-enabled, dns-enabled and default-route are
dropped (no equivalent in the new model)
modemd:
- load_config() replaces the infix-modem sysrepocfg call; reads from
ietf-hardware (modem/sim config), ietf-interfaces (bearer config),
and ietf-keystore (credentials) — three standard modules instead of
one proprietary one; modems absent from sysrepo config but present
in system.json are still started (physical detection wins)
- Default route written to /etc/net.d/<iface>.conf (picked up by
netd/staticd/zebra) instead of a direct 'ip route add'; routes are
now visible to FRR for redistribution and metric-based failover
- Addresses tagged with proto wwan (rt_addrprotos entry 7) for
precise flush on disconnect without touching other address sources
- _derive_gateway() handles point-to-point bearers where modem
reports gateway as "--"; derives peer from link-scope subnet route
- SimThread exits cleanly on platforms without /dev/simctrl
- sim-setup, modem-info, modem-udev, modem-rpc, modem-sms all ported
to the ietf-hardware/ietf-interfaces namespace
statd:
- infix_modem.py now emits modem-state and sim-state into ietf-hardware
component entries (modem0, sim0, ...) instead of a separate tree;
sim-state reports physical slot, lock state, and SIM operator name
- ietf_hardware.py: hardware component names deduplicated by rename
(cpu → cpu, cpu1, ...) to prevent LY_EVALID when hwmon and thermal
sysfs both normalise to the same sensor name
- show hardware: Cellular Modems and SIM Cards tables added; sensor
table width now adapts to the widest section in the output
- 00-probe: USB modem detection writes to system.json["modem"], same
source as wifi-radios, feeding gen-hardware which emits the
ietf-hardware component entries at factory config generation time
CLI:
- 'show modem [ref]': without ref shows Cellular Modems and SIM Cards
tables (same data subset as 'show hardware'); with ref shows a full
per-modem dump (hardware info, status, cellular, SIM, bearers, GPS)
- 'modem restart <ref>': disconnect and reconnect all bearers without
a full hardware reset
- 'modem reset <ref>': factory-reset the modem firmware
- 'modem sms <ref> <number> <message>': send an SMS via the modem
(uses the signalling plane; no active data bearer required)
- MODEMS PTYPE provides tab-completion reading modem names from
/run/system.json
doc:
- Add initial User Guide for modems
- Update ChangeLog with modem news
- Use absolute URLs in ChangeLog. Relative links only work for in-tree
references, not in release notes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
In the last major refactor of confd to a stand-alone daemon, new command
line options were added, one of which -v <LEVEL>. Unfortunately opening
up for a few corner cases where the debug flag or the log_level was not
set properly.
This fixes that and also adds a CONFD_ARGS for /etc/default/confd, which
is customary for all daemons.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
When an interface has been handed off to a container it lives in
another netns, so `ip neigh/addr flush dev FOO` fails on the host.
The failure aborts dagger, and since interfaces_change() runs before
containers_change() in change_cb(), the container delete path is
never reached -- the stale container keeps the interface trapped in
its netns, breaking the next reconfiguration.
Guard both the neighbor and address flush exit scripts with
`if_nametoindex(ifname)` -- true exactly when the interface is in
the host netns, false for both "in a container" and "already gone".
This replaces the pre-existing `!cni_find(ifname) && if_nametoindex(
ifname)` guard at the addr site: cni_find() added no information
for this check and would popen(container find) for nothing when the
interface had been deleted entirely.
Also harden wrap() in /usr/sbin/container so a stale setup pidfile
doesn't short-circuit Finit's stop attempt -- kill the setup PID and
still ask podman to stop the container.
Fixes#1493
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The U-Boot env file is generated by
mkenvimage, which prefixes the variable data with a 4-byte CRC32 by default.
ixpreboot.sh previously imported it with `env import -b` as per uboot docs(-b assume binary format (’0’ separated, “00” terminated).),
which treats the buffer as raw binary and does not strip the CRC.
As a result the first variable's name is parsed a part of CRC.
Later variables import correctly because by then the parser is past the CRC.
Switching to `env import -c` as per uboot docs (-c assume checksum protected environment format.)
makes U-Boot validate and strip the CRC before parsing, so the first variable imports under its real name.
Tested on:
- QEMU
Signed-off-by: Ejub Sabic <ejub1946@outlook.com>
On multi-chip DSA hardware (e.g., boards with multiple mv88e6xxx
chips), each switch chip has its own independent PHC device. With
boundary_clock_jbod enabled, ptp4l starts but only disciplines the
active slave port's PHC — the others drift.
Automatically start phc2sys -a alongside any BC or TC instance using
hardware timestamping. It subscribes to ptp4l's UDS socket, tracks
BMCA, and disciplines all non-active PHCs to match the active one.
On single-chip hardware it is a harmless no-op.
CLOCK_REALTIME is intentionally left untouched. Syncing the system
clock to PTP (phc2sys -rr), feeding the PHC from GPS/NTP (ts2phc,
phc2sys reverse), and full multi-source coordination (timemaster) are
planned as follow-on phases; see the issue tracker for the roadmap.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
NOTE: this is intended only for use on devboards like EspressoBin where
the onboard bootloader only understands fat and ext2 file systems.
To enable, use 'make apply-ext4'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Add board support for the BananaPi BPI-R4 router board, based on the
MediaTek MT7988A (Filogic 880) SoC with quad-core Cortex-A73 CPU.
The BPI-R4 is the successor to the BPI-R3 but uses entirely different
silicon: the internal switch runs at 2.5 GbE per port, the two SFP+
uplinks use native USXGMII at up to 10 Gbps, and WiFi is provided by
a MediaTek MT7996E PCIe module supporting tri-band WiFi 7 (2.4/5/6 GHz).
Two board variants, BPI-R4-2g5 and BPI-R4P, replace one SFP+ cage with
a 2.5 GbE RJ45 port (using the MT7988A internal 2.5G PHY); the R4P adds
an optional PoE daughterboard on that port. Both use the upstream
mt7988a-bananapi-bpi-r4-2g5 device tree.
Interfaces:
- Standard R4: eth1 (gmac1/sfp2 LAN cage) → sfp2, eth2 (gmac2/sfp1 WAN cage) → sfp1
- R4-2g5/R4P: eth1 (2.5G PHY) → wan, eth2 (gmac2/sfp1 WAN cage) → sfp1
Factory reset / WPS button (GPIO14, active-low): detected by U-Boot at
boot for factory reset; reused by Linux as KEY_WPS_BUTTON at runtime.
eMMC install note: the MT7988A eMMC (pins 38-49) and SD card (pins 32-37)
use separate physical pads so there is no electrical bus sharing, unlike
some BPi-R3 configurations. However, a single MMC controller
(mmc@11230000) handles both, so only one is active per boot session.
The eMMC install procedure therefore still requires an intermediate SPI
NAND bootloader step until a Linux-side install path is tested.
SD card build (bpi_r4_sd_boot_defconfig) now uses the new
mt7988a_bpir4_sd U-Boot defconfig with cap-sd-highspeed enabled,
giving 50 MHz SD transfers instead of the previous 25 MHz legacy mode.
Also adds MT7988/MT7996 firmware support to the buildroot linux-firmware
package, for 2.5 GbE port PHY and WiFi7 extension board, and updates the
aarch64_defconfig, aarch64_minimal_defconfig, the arch README, CI
boot/image workflows, mkimage.sh, and the ChangeLog.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Add --log-level command line option to filter out log messages from
lower log levels. Then fix the annoying CzechLight warning message
and useless "NACM config validation" log. Then add audit trail as
we have in netopeer2-server, and finish off by stripping redundant
fields from log message: timestamp, identity, and log level.
Fixes#892
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Previously svc_enadis() would call 'initctl enable + touch' for every
config change event, even when the service's enabled state was unchanged.
This caused rousette to be unnecessarily restarted on every test_reset(),
racing with the active RESTCONF connection on slow hardware.
Replace svc_enadis() and svc_change() with svc_enable() which only
manages nginx symlinks and calls 'initctl enable/disable' -- never touch.
Each handler now checks the diff for the specific leaf that changed:
- If /enabled appears in diff: call svc_enable() to start or stop it
- If other config leaves changed with service already enabled: touch only
This ensures rousette, ttyd, netbrowse, avahi, sshd, and lldpd are only
restarted when their configuration actually requires it.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
- Add x509-public-key-format identity to crypto-types
- Add certificate node to web services container
- Use certificate from ietf-keystore as web cert
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
netopeer2 and rousette were starting as soon as confd had a PID, while
confd was still loading the startup config via sr_replace_config(). This
held a write lock on the running datastore. A test calling test/reset
shortly after the NETCONF port opened would have its own sr_replace_config()
block on that lock for up to 60 s, at which point sysrepo timed out the
whole RPC with "SHM event 'rpc' processing timed out".
Fix by adding the usr/bootstrap condition to both management servers so
they don't accept connections until confd signals that bootstrap is complete.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Relocate probe of wifi radios from gen-hardware to 00-probe. This saves
one python invocation and some precious CPU cycles at boot.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Follow-up to 90f619b which first introduced the /etc/hostname.d concept.
This commit moves the setup of /etc/hostname.d to post-build.sh dropping
the initial call to the hostname activation script a bit, since it is
called anyway after bootstrap has finished. The scrip is also given a
bit of a refrehs, reducing overhead and needless log messages.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Infix technically does not need to start dbus and dnsmasq before confd
has loaded the system startup-config. So we move it in time to save
some CPU cycles for confd & C:o.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
On single-core Cortex-A7, the YANG bootstrap and config loading is the
dominant boot bottleneck. The current sequence spawns three serial
phases (bootstrap, sysrepo-plugind, load), each performing independent
sr_connect()/sr_disconnect() cycles, and every sysrepoctl/sysrepocfg
invocation is a fork+exec that rebuilds SHM from scratch.
Replace all three with a single confd binary that does one sr_connect()
and performs all datastore operations in-process:
- Wipe stale /dev/shm/sr_* for a clean slate
- sr_install_factory_config() from the generated JSON
- Smart migration: compare config version via libjansson, only
fork+exec the migrate script when versions actually differ
- Load startup-config (or test-config) via lyd_parse_data() +
sr_replace_config(), mirroring what sysrepocfg -I does internally
- On failure: revert to factory-default, load failure-config, set
login banners (Fail Secure mode)
- On first boot: copy factory-default to running, export to file
- dlopen plugins and enter event loop
The bootstrap shell script is split: config generation (gen-hostname,
gen-interfaces, etc.) stays in the new gen-config script, while all
sysrepo operations move into the C daemon. The finit boot sequence
collapses from 5 stanzas to 2 (gen-config -> confd).
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
- The biggest changes are syncing with latest BusyBox (busybox-update-config)
- Disable optimize for size
- Enable feature "SH_NOFORK" which allows /bin/sh to call applet_main()
directly without having to fork+exec busybox
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Set dhcp-authoritative so clients with unexpired leases are not NAKed
after a router crash or factory reset that clears the lease database.
Drop the untagged suppress directives for options 3 (router) and 6
(dns-server): they conflict with the global options confd writes to
dnsmasq.d/, causing spurious "Ignoring duplicate dhcp-option" warnings.
Per-subnet tagged options already suppress auto-detection, and without
an active dhcp-range there is nothing to suppress anyway.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Two races could prevent DHCP-learned default routes from being installed
at boot:
1. The signal from the DHCP client script could be lost leaving conf.d
updated but frr.conf stale.
2. Even when the signal was received, 'vtysh -b' could fail because the
FRR daemon chain (mgmtd→zebra→staticd) writes PID files before being
fully operational, causing netd to give up with no retry.
Fix both by refactoring netd to use libev:
- Use an inotify watch of CONF_DIR, so netd reacts directly to file
changes without depending on signal delivery.
- On backend_apply() failure, schedule a retry in 5s and call pidfile()
unconditionally so dependent services are not blocked waiting for the
Finit condition 'pid/netd' to be satisfied.
We also take this opportunity to rename /etc/netd/conf.d/ to /etc/net.d/
The extra /etc/netd/ directory level served no purpose — nothing else
lives there. Flatten to /etc/net.d/ which reads more naturally as the
drop-in directory for network configuration snippets.
Also, reduce logging a bit since each netd backend already logs success
or fail which is sufficient to know that a configuration change has been
applied or not.
Finally, with the new inotify processing in netd it's redundant to call
'initctl reload netd' from the udhcpc script, in fact it will only cause
unnecessary overhead, so we drop it.
Fixes#1438
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Two related problems prevented devices behind an intermediate hub (e.g.
the VIA Labs hub on the RPi 400's VL805 xHCI) from being authorized
when confd unlocks a USB bus:
1. Cascade ordering: usb_authorize() ran nftw() with FTW_DEPTH, which
visits children before the root-hub entry. The hub was authorized
while authorized_default was still 2, so when the kernel probed the
hub's children it immediately denied them. Fix: write
authorized_default=1 on the bus before entering nftw.
2. Slow/async probe: hub port enumeration is asynchronous — devices
behind a hub may appear in sysfs after nftw has already finished.
Fix: add a udev rule that catches usb_device add events on buses
where authorized_default=1 is already set on any ancestor root hub
and authorizes them immediately.
The ATTRS{} matcher in udev walks the full sysfs parent chain, so
authorized_default=1 on the root hub is visible even for devices several
hubs deep. Buses still locked at authorized_default=0 are left alone.
Also expand the docstring in generic_usb_ports() to document the
design: each root hub gets its own uniquely-named entry (USB, or USB1/
USB2/... when multiple) so buses can be individually controlled, and
boards needing finer control should use DT usb-ports annotations.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Replace the costly read-only filter loop (awk+tr per line) with a
stderr redirect, use /sys/class/net/ instead of ip+jq, and batch
all sysctl writes into a single call.
Reduces boot time by ~4s on 32-bit Arm systems.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Calling tune2fs for ext4 partitions at boot costs more than one second
boot time on 32-bit Arm systems, with very little gain. Dropping this
restores the default periodic fsck *and* saves boot time.
Instead of calling sgdisk four times, call it only when resizing and
instead use sysfs to find named paritions. Saves seconds on boards
with weaker CPU and slow media.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Increase Zebra NETLINK buffer to fix reported issues with zebra being
out of sync with kernel routes and interface changes.
Also, try '-a' option to allow other processes to delete zebra routes.
Other changes in this commit:
- Minor fixes to Finit service files for consistency
- Empty daemon.conf stub files to silence bogus mgmtd errors at startup
- Relocate all skeleton files from board/common to separate from files
that are actual Infix additions and what's package system integration
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
network.local is a discovery/browse page, not a management interface.
Serving it over HTTP eliminates the self-signed certificate warning
browsers show for .local mDNS names without changing the security
posture of the actual device management pages (still HTTPS-only).
The new port-80 server_name block takes precedence over the catch-all
HTTP → HTTPS redirect in default.conf.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Instead of publishing A/AAAA records for $(hostname).loocal with a CNAME
infix.local, we flip it around to take advantage of the mDNS conflict
resolution rules. This gives us infix.local for one device ont the LAN
and infix-2.local for the next.
Update all service records to *not* advertise hostname, but instead to
let Avahi imply that from the advertised A/AAAA and CNAME records.
Fixes#1387
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
DNS does not allow CNAME records at an apex domain (e.g. kernelkit.org),
so GitHub Pages cannot reliably redirect kernelkit.github.io to it. A
www subdomain can carry a CNAME pointing directly to kernelkit.github.io,
giving GitHub full control over the mapping and making the redirect work.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The weird `if env | grep -q 'reset-volumes'` construct is due to Klish
not sanitizing names before constructing variables, which leads to env
variables with '-' in them, not allowed in POSIX sh.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Rename BR2_PACKAGE_NETD_FRR to BR2_PACKAGE_NETD_FRR_GRPC to match the
naming convention of the other FRR backends (VTYSH, CONF) and make the
intent self-evident.
Have the gRPC backend select BR2_PACKAGE_FRR_GRPC so FRR is built with
gRPC support when — and only when — this backend is chosen.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This is a simpler approach than both grpc and watchfrr. Since we only
really need dynamic handling of static routes (for DHCP), we can track
changes made and maintain a "diff engine" for the resulting frr.conf.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
With the latest backported fixes to Finit, we no longer need to remove
/run/netd.pid to trigger a reload (restart) of staticd.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This includes adding a netd-daemon since FRR has gone towards all
daemons should be configured from mgmtd using YANG, netd uses gRPC to
reconfigure in mgmtd
Signed-off-by: Enable grpc
This update brings new support for editing cleartext-symmetric-keys with
the 'change' command. The cleartext-symmetric-key is of type binary and
its use differs between different key-formats.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Environment variables are stored in a separate .env file from the main
container script. The config SHA256 checksum only covered the script
file, so changes to env vars went undetected -- the container was not
recreated and kept running with stale values.
Include the env file contents in the config checksum calculation so that
any change to environment variables triggers a proper container restart.
Fixes#1313
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
All build scripts use $BR2_ARCH to determine location of critical board files
and configuration. As per previous decisions, the BR2_ARCH variable controls
all naming in Infix and for consistency the defconfig files are also renamed.
This fixes the issue with missing DTB file(s) for the Raspberry Pi 2B.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
RSSI is a vendor-specific relative index (0-255), while dBm is an
absolute power measurement. Since we report dBm values from iw,
rename the leaf to signal-strength for accuracy.