| Age | Commit message (Collapse) | Author |
|
A modem is on the bus while still probing, so a dial could start too early and
leave the interface without a session until something re-dialled it.
|
|
Two things kept an operator disconnect from taking effect.
vyos-netlinkd re-dials every configured WWAN interface that has lost its
session. It cannot tell a session lost to RF signal loss from one that was
taken down on purpose, so the disconnect was undone on the next reconcile
pass, at most 30 seconds later:
$ disconnect interface wwan0
08:53:33 t+10s state=registered
08:53:43 t+20s state=registered
08:53:53 t+30s state=connected <- re-dialled
08:54:03 t+40s state=connected
vyos-netlinkd: WWAN interface wwan0 is not connected, re-dialling...
vyos-netlinkd: Re-dialled WWAN interface wwan0
Record the intent instead of inferring it from the session state. op-mode
"disconnect" drops a marker in /run/vyos-wwan, which _reconcile_wwan() honours
and "connect" removes again. conf_mode removes it too, so a commit keeps
re-asserting the configured state - the marker only survives for as long as
nobody says otherwise, and never across a reboot.
|
|
/etc/cron.d/vyos-wwan re-ran the conf_mode script every 5 minutes via
op-mode "connect interface wwanN" to re-establish a session lost to RF
signal loss. Re-entering conf_mode from cron is unsafe: the cron job knows
nothing about the boot commit or about a commit in flight, so when a tick
lands before the running config is populated, get_config() returns nothing
but {'ifname', 'deleted'} and apply() takes the teardown branch instead of
dialling. It then stops the DHCP clients, stops ModemManager and unlinks its
own cron file, leaving wwan0 down with no way to recover until the next
commit:
CRON[1869]: (root) CMD (/usr/libexec/vyos/vyos-check-wwan.py)
systemd[1]: Stopping DHCP client on wwan0...
systemd[1]: Stopping Modem Manager...
This is timing dependent - it only bites when a */5 tick coincides with
boot - which is why it presents as an intermittently dead WWAN interface.
Move the re-dial into vyos-netlinkd, which already tracks these interfaces
for DHCP and already gates all of its work behind boot_configuration_complete()
and commit_in_progress2(). A re-dial can therefore no longer land on a
half-applied configuration. It also dials ModemManager directly rather than
re-entering conf_mode, so the teardown branch is no longer reachable from
the recovery path at all.
|
|
Original bug: while a commit held the lock, the daemon called sleep(1)
without reading the netlink socket. The pending message stayed queued
in the kernel buffer, so once the commit ended, a stale event (e.g. a
DOWN from a disable that had already been re-enabled) got processed as
if it were fresh - stopping dhclient on an interface that was actually
back UP.
The applied fix keeps only the two pieces that are actually
load-bearing:
1. Drain instead of sleep - while commit_in_progress2() is true, still
call ipr.get() to read and discard any pending netlink messages, so
nothing stale survives to be processed after the commit ends.
2. Reconcile on commit end - once the commit finishes, walk all
matching interfaces, compare live sysfs operstate against the last
state the daemon acted on (_iface_prev_operstate), and run the
normal DHCP handler for any that disagree. This catches a real
transition that happened only inside the drain window and would
otherwise be silently lost.
Dropped from the original fix: an unconditional live-sysfs override
inside _handle_dhcp_events() that ran on every event, not just around
commits - redundant with reconcile, and risked discarding genuine fast
link flaps unrelated to any commit.
Co-authored-by: Christian Breunig <christian@breunig.cc>
|
|
The state tracker starts empty whenever vyos-netlinkd starts, so the first same-state RTM_NEWLINK UP notification is treated as a transition and unnecessarily restarts DHCP.
Seed matching interface states from the kernel link dump after binding the rtnetlink socket. This preserves real DOWN-to-UP handling while suppressing the first redundant UP notification.
|
|
Complete the safer-subprocess migration started by the cmdl()/ifconfig
refactoring and convert every remaining vyos.utils.process.cmd() call site to
the list-based cmdl().
Drop the vyos.utils.process.cmd() implementation as it is no longer in use.
|
|
The previous vyos-netlinkd implementation for QoS policy re-apply was very heavy.
It conducted a full CLI validation and re-apply on every interface. Instead we
do not only re-apply the QoS configuration to the interface which has had an
address change detected by vyos-netlinkd.
This can be tested by checking "tc qdisc show" before disconnecting a PPPoE
interface and during/after reconnect. There will be no qdisc until the dynamic
interface has received an IP address - then the qdisc will be re-applied.
|
|
Re-apply QoS after dynamic interfaces get addresses after connect/disconnect.
When PPPoE interfaces re-connect we need to re-do QoS settings.
|
|
DHCP restarts
_handle_dhcp_events() restarts dhclient/dhcp6c on every RTM_NEWLINK
that carries operstate=UP, regardless of whether the interface was
already UP. Normal kernel events that re-notify UP without a preceding
DOWN (e.g. post-migration gratuitous-ARP, promiscuous-mode toggles)
trigger unnecessary DHCP restarts. Each restart also creates a feedback
loop: dhclient-script runs "ip link set dev <if> up" during PREINIT,
which emits another RTM_NEWLINK(UP), which triggers another restart.
One seed event becomes 10+ restarts in 15 seconds.
Add a module-level dict tracking the last observed operstate per
interface. Only proceed with the DHCP restart when the transition is
DOWN-to-UP or when no previous state is recorded (first boot / service
restart). UP-to-UP re-notifications are suppressed with a LOG_DEBUG
message.
Validated on a production VyOS 2026.05.26-1327-rolling router: a
controlled KVM live migration after the patch produced zero DHCP
restarts (all UP-to-UP events suppressed), with no impact on normal
DOWN-to-UP DHCP recovery.
|
|
Not all pyroute2 versions which ship IPRoute() support subscribing to RTNL
multicast groups. If subscribing fails, fallback to all messages.
|
|
Move to the re-implementation of the commit in progress check added in
commit 002d45b70efd ("vyos.commit: T8781: move from O(n) to O(1) for
commit_in_progress() checks").
|
|
message (#5199)
* vyos-netlinkd: T8781: do not call commit_in_progress() for EVERY netlink message
Signed-off-by: Fabrizzio Petrucci <fabrizziopm@compumundohipermegared.one>
* vyos-netlinkd: T8781: restrict netlink subscription to link multicast group
Bind IPRoute with RTMGRP_LINK only so route/rule/neighbour/address updates
(e.g. full-table BGP) are not delivered to this socket.
Add a match wildcard so unexpected message types do not raise MatchError and
get logged as generic errors.
---------
Signed-off-by: Fabrizzio Petrucci <fabrizziopm@compumundohipermegared.one>
Co-authored-by: Fabrizzio Petrucci <fabrizziopm@compumundohipermegared.one>
|
|
Failures appear during PPPoEIf.remove() -> flush_addrs() -> set_dhcpv6(False),
where disable uses self._cmd(f'systemctl stop ...'), which raises on any
non-zero exit code of cmd().
PermissionError: [Errno 1] is misleading: cmd() raises OSError(exit_code,
feedback); exit code 1 maps to PermissionError in Python 3, so logs point at
"permissions" while the real signal is systemctl stop returned 1 (job failure,
timeout, restart contention with Restart=always on dhcp6c@.service.
Add a small systemd teardown helper stop_systemd_unit() validating the return
codes from the "systemctl stop" calls.
|
|
strace: Process 16362 attached
[pid 16362] execve("/bin/cli-shell-api", ["/bin/cli-shell-api", "--show-active-only", "showConfig", "interfaces"], ...) = 0
[pid 16362] +++ exited with 0 +++
--- SIGCHLD ...
strace: Process 16363 attached
[pid 16363] execve("/bin/cli-shell-api", ["/bin/cli-shell-api", "--show-active-only", "showConfig", "interfaces"], ...) = 0
[pid 16363] +++ exited with 0 +++
--- SIGCHLD ...
This would appear to be due to vyos-netlinkd calling op_mode_config_dict() more
than is necessary: it can be called only when it may be applicable (this patch).
Co-Authored-by: John Estabrook <jestabro@vyos.io>
|
|
Implementing a daemon that listens for netlink messages in Python was discussed
for many years. This is a proof-of-concept implementation how we can listen for
netlink messages and process them in Python.
Python 3.10 minimum is required due to the use of case statements which mimics
C-style switch/case instructions.
Add example:
set interfaces ethernet eth1 vif 21 address dhcp
set interfaces ethernet eth1 vif 21 address dhcpv6
commit
If network cable is unplugged:
vyos-netlinkd[12681]: RTM_NEWLINK -> eth3.10, state=DOWN, mac=00:50:56:b3:9d:8e
vyos-netlinkd[12681]: Stopping dhclient@eth3.10.service...
vyos-netlinkd[12681]: Stopping dhcp6c@eth3.10.service...
If cable is plugged back in:
vyos-netlinkd[12681]: RTM_NEWLINK -> eth3.10, state=DOWN, mac=00:50:56:b3:9d:8e
vyos-netlinkd[12681]: RTM_NEWLINK -> eth3.10, state=UP, mac=00:50:56:b3:9d:8e
vyos-netlinkd[12681]: Restarting dhclient@eth3.10.service...
vyos-netlinkd[12681]: Restarting dhcp6c@eth3.10.service...
|