summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-09-24 15:27:27 +0000
committerChristian Breunig <christian@breunig.cc>2026-09-24 15:27:27 +0000
commit112fb62b8b15653c0a26e0de56f326cd2d67da9b (patch)
tree58c5cbfa31d0ee3e22f5d5f8f07a8d6c24295739 /src
parentf148cc7c47a7fc6f81a06817de5f24e0836d0792 (diff)
downloadvyos-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-xsrc/conf_mode/interfaces_wwan.py6
-rwxr-xr-xsrc/op_mode/connect_disconnect.py28
-rwxr-xr-xsrc/services/vyos-netlinkd8
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