diff options
| author | Christian Breunig <christian@breunig.cc> | 2026-09-24 15:27:27 +0000 |
|---|---|---|
| committer | Christian Breunig <christian@breunig.cc> | 2026-09-24 15:27:27 +0000 |
| commit | 112fb62b8b15653c0a26e0de56f326cd2d67da9b (patch) | |
| tree | 58c5cbfa31d0ee3e22f5d5f8f07a8d6c24295739 /src | |
| parent | f148cc7c47a7fc6f81a06817de5f24e0836d0792 (diff) | |
| download | vyos-1x-112fb62b8b15653c0a26e0de56f326cd2d67da9b.tar.gz vyos-1x-112fb62b8b15653c0a26e0de56f326cd2d67da9b.zip | |
wwan: T9350: properly disconnect "disconnect interface wwanN"
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.
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/interfaces_wwan.py | 6 | ||||
| -rwxr-xr-x | src/op_mode/connect_disconnect.py | 28 | ||||
| -rwxr-xr-x | src/services/vyos-netlinkd | 8 |
3 files changed, 39 insertions, 3 deletions
diff --git a/src/conf_mode/interfaces_wwan.py b/src/conf_mode/interfaces_wwan.py index 7e02d9d7a..dd5a1e8c3 100755 --- a/src/conf_mode/interfaces_wwan.py +++ b/src/conf_mode/interfaces_wwan.py @@ -30,6 +30,7 @@ from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import WWANIf from vyos.utils.network import is_wwan_connected from vyos.utils.process import cmdl +from vyos.utils.wwan import clear_admin_disconnected from vyos.utils.wwan import connect_options from vyos.utils.wwan import modem_connect from vyos.utils.wwan import modem_disconnect @@ -117,6 +118,11 @@ def generate(wwan): return None def apply(wwan): + # A commit re-asserts the configured state, so it also takes the interface + # out of the administratively disconnected state that op-mode "disconnect + # interface wwanN" put it in. + clear_admin_disconnected(wwan['ifname']) + # ModemManager is required to dial WWAN connections - one instance is # required to serve all modems. Activate ModemManager on first invocation # of any WWAN interface. diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py index 17cf9d7e0..9b43e4fd2 100755 --- a/src/op_mode/connect_disconnect.py +++ b/src/op_mode/connect_disconnect.py @@ -19,10 +19,13 @@ import argparse from psutil import process_iter +from vyos.ifconfig import WWANIf from vyos.utils.process import call from vyos.utils.commit import commit_in_progress from vyos.utils.network import is_wwan_connected -from vyos.utils.process import DEVNULL +from vyos.utils.wwan import clear_admin_disconnected +from vyos.utils.wwan import modem_disconnect +from vyos.utils.wwan import set_admin_disconnected def check_ppp_interface(interface): if not os.path.isfile(f'/etc/ppp/peers/{interface}'): @@ -52,6 +55,9 @@ def connect(interface): print(f'Interface {interface}: connecting...') call(f'systemctl restart ppp@{interface}.service') elif interface.startswith('wwan'): + # Take the interface out of the administratively disconnected state, so + # vyos-netlinkd re-dials it again should the session be lost later on + clear_admin_disconnected(interface) if is_wwan_connected(interface): print(f'Interface {interface}: already connected!') else: @@ -72,11 +78,27 @@ def disconnect(interface): print(f'Interface {interface}: disconnecting...') call(f'systemctl stop ppp@{interface}.service') elif interface.startswith('wwan'): + # Keep the interface down until it is connected again. vyos-netlinkd + # re-dials every configured WWAN interface that lost its session, and + # would otherwise undo this disconnect on its next reconcile pass. The + # marker is set even when the session is already down, so a disconnect + # issued while the modem happens to be searching for a network is not + # silently ignored. + set_admin_disconnected(interface) if not is_wwan_connected(interface): print(f'Interface {interface}: connection is already down') else: - modem = interface.lstrip('wwan') - call(f'mmcli --modem {modem} --simple-disconnect', stdout=DEVNULL) + print(f'Interface {interface}: disconnecting...') + modem_disconnect(interface, quiet=True) + + # Dropping the bearer does not touch the netdev - a cdc_mbim/qmi_wwan + # interface stays IFF_UP with carrier and an operstate of "unknown" + # once ModemManager is done, so it would keep reporting u/u and hold + # on to the address the DHCP client got for the session that just + # ended. Tear it down the same way conf_mode does for "disable". + w = WWANIf(interface) + if w.exists(interface): + w.remove() else: print(f'Unknown interface {interface}, cannot disconnect. Aborting!') diff --git a/src/services/vyos-netlinkd b/src/services/vyos-netlinkd index 5cce1ceff..4c91561d2 100755 --- a/src/services/vyos-netlinkd +++ b/src/services/vyos-netlinkd @@ -43,6 +43,7 @@ from vyos.utils.process import cmdl from vyos.utils.process import is_systemd_service_active from vyos.utils.process import stop_systemd_unit from vyos.utils.wwan import connect_options +from vyos.utils.wwan import is_admin_disconnected from vyos.utils.wwan import modem_connect from vyos.utils.wwan import modem_disconnect from vyos.utils.wwan import start_modem_manager @@ -370,6 +371,13 @@ def _reconcile_wwan() -> None: _wwan_clear_backoff(ifname) continue + # Do not undo an explicit op-mode "disconnect interface wwanN" - the + # session stays down until it is connected again or the configuration + # is re-applied by a commit. + if is_admin_disconnected(ifname): + _wwan_clear_backoff(ifname) + continue + if is_wwan_connected(ifname): _wwan_clear_backoff(ifname) continue |
