Setting just preferred-mode in YANG (no allowed-mode leaf-list) built an
invalid mode string with an empty allowed:
Mode 'allowed: ; preferred: 4g' is not supported
The empty allowed: can never match anything in the modem's supported-modes
list, so the user got a cryptic 'is not supported' even when their preferred
mode was, in principle, supported.
When only preferred-mode is configured, walk the modem's supported-modes list
for an entry that already has that preferred, and reuse its allowed set. If
no supported entry has that preferred mode, fail with a message that lists
every combination the modem does support, easier to diagnose than the silent
'empty allowed' case.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Some cellular modems (notably Quectel EM05) don't expose a RESET capability
over MBIM/QMI. Both 'mmcli --reset' and 'mmcli --factory-reset' return
Unsupported immediately, the AT command is never forwarded to the firmware,
and there's nothing modemd can do over the management bus to recover. Until
now modemd's failed-state retry kept calling --reset every 30 s for the rest
of uptime, producing pure log noise:
modemd: <error> [modem0] Resetting after being 30s in state failed
modemd: <info> [modem0] Resetting
ModemManager: <wrn> [modem0] failed requesting modem reset: Unsupported
ModemManager: <wrn> [modem0] failed requesting modem factory reset: Unsupported
modemd: <error> [modem0] Unable to reset
... and the same five lines again 30 s later, indefinitely.
Mitigate this by caching the Unsupported state per modem per session.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Cellular modems (Quectel EM05/EM06/EM12, Sierra EM7565, ...) expose
GPS data as NMEA on a dedicated USB serial interface. Until now we
relied on ModemManager's --location-enable-gps-nmea, which makes MM
hold the NMEA port and parse the stream itself — useful for mmcli
but a dead end for the rest of the system. The result was that
modem GPS was visible only via 'show modem' and could not act as an
NTP fallback time source on devices without dedicated GPS hardware.
This commit reroutes the NMEA stream into the existing
gpsd → chronyd pipeline so cellular modems behave just like any
other NMEA-over-USB GPS dongle. No changes to gpsd, chronyd, or
the ietf-hardware:gps component — the user activates it the same
way as for a dedicated receiver, by adding a 'gps' hardware
component that references /dev/gpsN.
This is Phase 2 of the modem GPS / location integration plan
(.notes/modem-gps-plan.md).
udev (src/modemd/77-mm-modem-gps.rules):
- For known vendor:product:interface tuples, set ID_MM_PORT_IGNORE
so ModemManager keeps off the NMEA port, and add
SYMLINK+="gps%n" so gpsd's existing udev hook picks up the
device automatically.
- Interface-number match uses ENV{ID_USB_INTERFACE_NUM} rather
than ATTRS{bInterfaceNumber}: udev requires all ATTRS{} matches
in a rule to share one parent, but idVendor/idProduct live on
the USB device while bInterfaceNumber lives on the USB
interface. ENV{} matching has no parent constraint and udev
already populates ID_USB_INTERFACE_NUM from bInterfaceNumber.
- Initial entries: Quectel EM05 (2c7c:0125), EM06-E (2c7c:0306),
EM12-G (2c7c:0512), Sierra Wireless EM7565 (1199:9091).
Easy to extend.
modemd (modemd/__init__.py):
- GPS_AT_COMMANDS table maps manufacturer -> (enable, disable) AT
command pair. prepare_location() issues the vendor AT command
via 'mmcli --command=AT+QGPS=1' (or AT+CGPS=1 for Sierra)
instead of --location-enable-gps-{nmea,raw}. Vendor lookup is
substring-based ('Quectel' matches both 'Quectel' and 'Quectel
Incorporated') because ModemManager normalises differently
across firmware revisions. For unrecognised vendors the
high-level MM path is still used as a fallback, so nothing
regresses for hardware not yet in the table.
- The AT path runs based on the udev/vendor table, not on MM's
--location-status capabilities. Setting ID_MM_PORT_IGNORE on
the NMEA port removes 'gps' from MM's capability list even
though the GPS hardware is still there, so a capability gate
would mistakenly skip GPS for the very modems we're targeting.
- _location_capabilities() filters MM-managed sources so a single
unsupported flag (e.g. CDMA on an LTE-only modem) doesn't fail
the whole batched call. Sources the modem doesn't support are
silently skipped; a warning is logged only if the user's YANG
config explicitly lists one.
- Subprocess fan-out collapsed: agps-msa, agps-msb, 3gpp and cdma
flags share a single batched mmcli invocation.
prepare_location() drops from 5 subprocess calls to 1 (unknown
vendor) or 2 (known vendor, AT + batched MM).
- Stringly-typed if/elif source dispatch replaced with a
dict-driven table (LOCATION_MM_FLAGS).
- Two helpers eliminate the ['mmcli', '-m', self.path, ...]
boilerplate at ~30 ModemThread call sites:
self._mmcli(*args, check=True) # runcmd wrapper
self._mmclij(*args) # runcmdj wrapper
build (package/feature-modem/Config.in):
- Select BR2_PACKAGE_MODEM_MANAGER_ATVIADBUS so ModemManager
accepts raw AT commands over D-Bus without being started in
--debug mode. Required by the AT path above; without it
'mmcli --command' is rejected with MM_CORE_ERROR_UNAUTHORIZED.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Cellular modem location data (GPS coordinates, 3GPP cell info) was
previously CLI-only — 'show modem modem0' shelled out to modem-info
which queried mmcli on demand and rendered straight to terminal.
NETCONF/RESTCONF clients had no view into modem location, and there
was no way to tell how stale any modem-state field was.
This commit moves both into the YANG operational tree so they are
accessible through every northbound interface, not just the CLI. It
is Phase 1 of the modem GPS / location integration plan; Phase 2 will
route the modem's NMEA TTY into the existing gpsd → chronyd pipeline
so cellular modems can also serve as an NTP fallback time source.
YANG (infix-hardware.yang, revision 2026-05-10):
- modem-state/last-change (yang:date-and-time): freshness indicator
for the modem-state subtree, bumped on each modemd poll cycle
- modem-state/location-state container: source (gps|3gpp|...),
latitude/longitude/altitude (decimal64 6/6/1 fraction-digits),
cell-id/lac/tac/mcc/mnc, and its own last-change
Runtime path:
- modemd's check_location() now writes
/run/modemd/modemN/location/data.json with the full location data
plus an RFC3339 last-change. Atomic via tmp+rename. Walks the
mmcli output once instead of twice.
- check() touches /run/modemd/modemN/state.json with a fresh
last-change after each successful state poll.
- yanger/infix_modem.py reads both files via HOST.read_json and
folds them into the modem-state operational subtree.
- cli_pretty show_modem_detail rewritten to consume the operational
tree (same path as 'show modem' overview), with new 'Last Update'
lines for both top-level state and location.
- bin/show/modem() consolidated: both 'show modem' and
'show modem REF' use get_json('/ietf-hardware:hardware') — no
more direct modem-info subprocess call.
Cleanup along the way:
- Removed the legacy /run/modemd/modemN/location/{up,down,disabled,
failed} marker file writes — they had no readers anywhere in the
tree. In-memory self.location['state'] is kept purely as the
edge-trigger for the 'Retrieved GPS location' log line.
- Aligned now_rfc3339() output with yanger.common.YangDate (+00:00
form, not Z) so consumers see one consistent yang:date-and-time
format regardless of producer.
- Fixed multi-modem SIM scoping in show_modem_detail: now derives
sim<N> from modem<N> instead of picking the first SIM globally.
The bearer / hardware-revision / phone-number / supported-carrier
sections of 'show modem REF' are deliberately omitted for now —
those fields are not yet in the YANG tree. Bearer details in
particular likely belong on the wwan interface
(ietf-interfaces:interfaces-state) rather than on the modem hardware
component, and can be addressed in a follow-up.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
On SIGHUP, modemd stops all ModemThreads, re-runs sim-setup, then
restarts the modem threads from fresh sysrepo config. The pidfile is
touched with os.utime() once the reload is complete, satisfying Finit's
default pidfile-based notify so confd can signal a reload and wait for
it to finish before proceeding.
This allows operator and APN changes to take effect without a full
daemon restart.
Also extract start_modem_threads() to remove the duplicated thread
startup loop shared between initial startup and reload, and align
sighandler() to use isinstance(th, ModemThread) consistently with the
rest of the thread-management code.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Python scripts without a .py extension are never cached as bytecode by
CPython — every invocation re-parses the source. Packaging as a wheel
pre-compiles all modules to .pyc and installs thin import stubs as the
executables, matching the approach used by bin/show and statd.
Restructure the modemd Python scripts into a proper Python package
(src/modemd/modemd/): each script becomes a module with a main() entry
point. pyproject.toml declares 11 entry points; the Buildroot
MODEMD_BUILD_PYTHON hook builds a wheel via PEP 517, then pyinstaller.py
installs compiled stubs to /usr/libexec/modemd/ and pre-compiled .pyc
modules to site-packages.
/sbin/modemd is now a symlink to the compiled stub.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
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>
defconfig.sh used $# (find result, incl. *~ / *.bak) for the TAP total
but then 'continue'd past backups without decrementing — so a stray
bpi_r4_*_defconfig~ in the tree made TAP see "1..23" followed by only 21
results, and the harness treated the missing 2 as failures. Filter the
patterns at find instead; $# now matches what the loop iterates over
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Three bugs found while testing modem support on hardware:
ietf_system.py: "interface": null written to DNS server entries when
no interface is associated, causing LY_EVALID in the ietf-system
schema validator. Only include the key when the interface name is
non-empty.
ietf_system.py: empty list inserts (user [], server [], options [],
search []) overwrote configured data in the operational store instead
of leaving it intact. Guard each insert so operational data only
shadows configured data when there is something to report.
show system: CPU temperature reported only the first sensor named
exactly "cpu", "soc", or "core". Platforms with multiple thermal
zones (e.g. BPI-R4 exposes cpu and cpu1) always showed only one
reading. Collect all sensors whose name matches a CPU/SoC prefix
and report the maximum.
statd.c: yanger parse errors logged without model name or libyang
error string, making failures hard to diagnose. Include both.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Integrates the modem management subsystem, donated by Avvero Pty Ltd,
adapted to work on any platform without proprietary hardware (simctrl,
modules.json, /sys/class/sim/).
Components:
- package/feature-modem: Buildroot feature package pulling in modemd,
ModemManager, libmbim, libqmi, and USB serial/MBIM/QMI kernel drivers
- package/modemd: Buildroot package for the modemd daemon and helpers
- src/modemd: modem management daemon — detects modems via ModemManager,
connects bearers, configures IP addresses, handles SMS and location
- src/confd/src/modem.c: confd plugin — enables/disables modemd and
modem-manager via sysrepo, forwards RPCs (restart/reset/send-sms)
- src/confd/src/interfaces.{c,h}: IFT_MODEM type for wwan* interfaces;
confd manages address/route assignment, modem daemon owns link state
- src/confd/yang/confd/infix-modem.yang: YANG model for modem config and
operational state (bearers, APN, bands, modes, location, SMS)
- src/statd/python/yanger/infix_modem.py: operational state collector
- patches/modem-manager: upstream patches for udev install path,
multiplexed interface naming, and MBIM port trust from udev hint
(the last fixes Dell DW5811e / Sierra EM7455 on cold boot)
- patches/libqmi: ignore sysfs in download mode
Platform portability fixes for boards without simctrl hardware:
- modem-info: fall back to /run/modems.json when /run/modules.json is
absent; discover wwan interfaces via sysfs scan; provide synthetic SIM
entry when /sys/class/sim/ is not present; fix dbg() to respect flag
- modemd: make --set-allowed-modes=any best-effort (some modems only
support one mode combination and reject any change); skip mode set
when current mode already matches requested mode
- sim-setup: guard simctrl open, return None when modules.json absent
to skip SIM slot switching entirely on non-simctrl hardware
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>
The M.2 LTE slot uses the USB2 bus. Add it as an unlocked component so
the modem hardware is accessible and visible to users in the config.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
hostapd limit hard what BSSID to use, this since hosatpd is desinged
to use radio-mac+n as default. We demand the user to select the MAC
address, if having multiple BSS.
Material's defaults (Roboto, bluish slate dark mode) looked dull next
to the Chirpy-based kernelkit.org blog. Match its typography and colors
via extra.css, no engine change:
- Lato headings + Source Sans 3 body + system monospace for code
- Dark mode: neutral near-black background, soft-gray text, darker code
blocks, Chirpy blue links (replacing Material's high-contrast slate)
- Light mode: warmer body text, blue links, tinted sidebars
- Keep the orange brand on the masthead; links use Chirpy blue
Set theme.font: false so Material no longer also fetches Roboto.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Extend the existing 'show <running|startup|factory>-config' family with an
optional 'path /XPATH' argument that maps straight to the underlying 'copy
-x' flag, and add 'show operational [path /XPATH]' for symmetry with the
configuration datastores.
admin@example:/> show running-config path /ietf-interfaces:interfaces
admin@example:/> show operational path /ietf-system:system-state
Also, improve error feedback from the copy command slightly.
Before:
admin@example:~$ copy running -x /foo
Error: (null) (5)
Error: failed retrieving running-config data
After:
admin@example:~$ copy running -x /foo
Error: Couldn't parse path "/foo": Prefix "foo" not defined. (5)
Error: failed retrieving running-config data
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
cli-pretty prints a friendly message on stdout before any sys.exit(1)
— 'Interface "w" not found', 'Error, top level "ietf-routing:routing"
missing', etc. The wrapper used subprocess.run(..., check=True) and
on non-zero exit caught CalledProcessError, throwing away the captured
stdout and printing the generic exception message instead.
Drop the check=True / try-except dance, always relay stdout, and only
fall back to the generic 'Error running cli-pretty' line when the
subprocess crashed without producing any output.
Before:
admin@bpi:/> show interface w
Error running cli-pretty: Command '['/usr/libexec/statd/cli-pretty',
'show-interfaces', '-n', 'w']' returned non-zero exit status 1.
After:
admin@bpi:/> show interface w
Interface "w" not found
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The Ethernet packet/RMON counters at the bottom of 'show interface
<name>' were appended with a wider label column than the operational
data above them, breaking visual alignment. Group them under an
"Ethernet Statistics" section heading (Decore.title), same convention
as 'Available Networks' in the wifi scan view.
Narrow the operational-data label column from 20 to 19 chars so the
title bar exactly matches the heading text — a tighter and more
deliberately aligned look. All labels in pr_iface fit within 19
chars except 'lacp system priority' (20); rename it to 'lacp sys
priority' for consistency with the other 'lacp ...' labels (lacp
aggregate id, lacp partner mac, ...) so the new field width works
universally.
Refresh CLI snapshots and doc samples to match.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Fix#1506: restore the VLAN interface documentation that was lost when
networking.md was split into per-topic pages. Content adapted from the
v25.11.0 networking.md VLAN section, plus a new "Stacked (Q-in-Q)"
section showing `eth0.10.20`-style nesting and the layered `show
interface` rendering of the parent chain.
Update the interface-type table in networking.md to point at the new
vlan.md instead of the dangling ethernet.md#vlan-interfaces anchor.
ChangeLog: document the user-facing changes on this branch (YANG model
upgrade, layered show interface output, auto-negotation typo fix) and
the new VLAN documentation.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Update CLI samples across the documentation to the new layered
presentation: physical-medium row when linked (1000baseT/...), bare
MACs, and key: value DATA tokens. Add a short prose block in
ethernet.md and iface.md explaining the layering.
Drop the historical 'show interfaces' (plural) form — removed from
klish years ago — from all user-facing examples and the landing page.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Import ieee802-ethernet-interface@2025-09-10.yang, from IEEE 802.3.2-2025,
and companion ieee802-ethernet-phy-type.yang, replacing the 2019 revision.
Operational interface speed moves to ietf-interfaces:speed, RFC 8343,
eth:speed is now obsolete. Bump infix-interfaces.yang revision to track
the upgrade.
The CLI 'show interface' command renders interfaces as bottom-up layered
protocol rows. Ethernet interfaces a new IEEE link-mode row (1000baseT,
10GbaseLR, ...) is added with 'duplex: full|half' in the DATA column.
Without link the ethernet row is the first row. Tunnels and wifi follow
the same layering: gre/vxlan rows carry remote:/vni: tokens with optional
ethernet sub-row for the L2-bearing variants; wifi rows carry 'station' or
'access-point' ssid:, signal:, stations: tokens.
admin@bpi-76-8f-c2:/> show interface
⚑ INTERFACE PROTOCOL STATE DATA
⇅ lo loopback UP
ipv4 127.0.0.1/8 (static)
ipv6 ::1/128 (static)
⇅ br0 bridge DOWN
│ ethernet 32:2e:3f:76:8f:c2
│ ipv4 192.168.0.1/24 (static)
⇅ ├ lan0 bridge LOWER-DOWN
⇅ ├ lan1 bridge LOWER-DOWN
⇅ ├ lan2 bridge LOWER-DOWN
⇅ └ lan3 bridge LOWER-DOWN
⇅ gre0 gre UP remote: 198.51.100.7
ipv4 10.255.0.1/30 (static)
⇅ sfp1 ethernet DOWN ca:59:f0:77:80:5b
⇅ wan 1000baseT UP duplex: full
ethernet 32:2e:3f:76:8f:c2
ipv4 192.168.0.235/24 (dhcp)
ipv6 fe80::302e:3fff:fe76:8fc2/64 (link-layer)
⇅ wan.10 vlan UP vid: 10
│ ipv4 10.0.10.1/24 (static)
└ wan
⇅ wan.10.20 vlan UP vid: 20
│ ipv4 10.0.10.20/28 (static)
└ wan.10
⇅ wifi0 wifi UP station ssid: corp-net signal: good
ethernet dc:a6:32:00:11:22
ipv4 192.168.7.42/24 (dhcp)
yanger populates phy-type and pmd-type from a static ETHTOOL link-mode
identity table keyed on (port, speed, duplex). Both leaves are populated
for unambiguous media (copper, DAC, copper-T); for generic fiber where
the (port, speed, duplex) tuple can map to multiple PMDs (SR vs LR vs
ER, etc.) only the phy-type family identity is emitted — guessing the
PMD would be misleading. cli_pretty prefers pmd-type (specific) and
falls back to phy-type (family) for display.
yanger also emits ietf-interfaces:speed and fixes a long-standing
'auto-negotation' typo (yanger had never matched ethtool's correctly-
spelled JSON field, so operational auto-negotiation always read
'unknown').
Filling the WG's gap on fixed-speed config
------------------------------------------
IEEE Std 802.3.2-2025 obsoleted the eth:speed leaf without providing a
config-true replacement; the standards-correct way to express what
older Infix configurations called 'fixed speed' is to restrict the set
of PMDs auto-negotiation may advertise. Augment auto-negotiation with
two leaf-lists:
auto-negotiation/advertised-pmd-types config-true, leaf-list of pmd-type
ethernet/supported-pmd-types config-false, leaf-list of pmd-type
Pinning a port to a single mode is now expressed as a single-entry
advertised-pmd-types list. The kernel-supported set is exposed as
operational state for diagnosis — SFP/SFP+ cages report the inserted
module's capabilities, narrowing the list to a single PMD when the
optic only supports one mode (as suggested by @wkz in PR review).
When the supported list collapses to a single entry, yanger uses it
to refine the operational pmd-type leaf — more accurate than the
(port, speed, duplex) lookup for fiber.
confd C apply path (src/confd/src/ieee802-ethernet-interface.c)
translates each advertised pmd-type identity to the corresponding
ETHTOOL_LINK_MODE_*_BIT_*, ORs them into a mask, and emits
'ethtool --change <if> autoneg on advertise 0x<mask>'. An empty
list keeps the historical default of advertising every supported
mode. The (legacy) 'auto-negotiation/enable = false' + speed/duplex
branch is removed — it has no model representation any more.
cli_pretty's detail view gains 'advertised' and 'supported' rows
listing the PMDs in each set. A new _pr_label_list() helper unifies
multi-row rendering across these new rows and the pre-existing
ipv4/ipv6 address rows.
The detailed interface view also gains a 'link mode' row carrying the
pmd-type / phy-type-derived label.
Migration of existing startup configurations
--------------------------------------------
Bump confd version 1.8 → 1.9 to trigger the migration on upgrade.
share/migrate/1.9/10-ethernet-advertise.sh translates old configs of
the form
ethernet { auto-negotiation { enable false; } speed S; duplex D; }
into
ethernet { auto-negotiation { advertised-pmd-types [PMD]; } duplex D; }
mapping (S, D) → PMD via a static lookup covering the copper-T speeds
the deprecated speed leaf historically supported (10/100/1000/2.5G/
5G/10G). Interfaces that don't disable auto-negotiation, or that
lack a speed leaf, are passed through untouched.
Closes#530Closes#805
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
bridge -j vlan global show can return VLAN ranges as
{"vlan": 1, "vlanEnd": 3} instead of individual entries.
The vlans dict was only keyed on the start value, causing
port VLANs 2 and 3 to be reported as "Unexpected".
Also fix typo in error message (brpvlans -> brpvlan).