diff options
Diffstat (limited to 'src/conf_mode/interfaces-wwan.py')
-rwxr-xr-x | src/conf_mode/interfaces-wwan.py | 97 |
1 files changed, 69 insertions, 28 deletions
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py index a4b033374..9a33039a3 100755 --- a/src/conf_mode/interfaces-wwan.py +++ b/src/conf_mode/interfaces-wwan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2021 VyOS maintainers and contributors +# Copyright (C) 2020-2022 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -21,8 +21,10 @@ from time import sleep from vyos.config import Config from vyos.configdict import get_interface_dict +from vyos.configdict import leaf_node_changed from vyos.configverify import verify_authentication from vyos.configverify import verify_interface_exists +from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_vrf from vyos.ifconfig import WWANIf from vyos.util import cmd @@ -36,7 +38,7 @@ from vyos import airbag airbag.enable() service_name = 'ModemManager.service' -cron_script = '/etc/cron.d/wwan' +cron_script = '/etc/cron.d/vyos-wwan' def get_config(config=None): """ @@ -50,6 +52,32 @@ def get_config(config=None): base = ['interfaces', 'wwan'] wwan = get_interface_dict(conf, base) + # We should only terminate the WWAN session if critical parameters change. + # All parameters that can be changed on-the-fly (like interface description) + # should not lead to a reconnect! + tmp = leaf_node_changed(conf, ['address']) + if tmp: wwan.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['apn']) + if tmp: wwan.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['disable']) + if tmp: wwan.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['vrf']) + # leaf_node_changed() returns a list, as VRF is a non-multi node, there + # will be only one list element + if tmp: wwan.update({'vrf_old': tmp[0]}) + + tmp = leaf_node_changed(conf, ['authentication', 'user']) + if tmp: wwan.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['authentication', 'password']) + if tmp: wwan.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['ipv6', 'address', 'autoconf']) + if tmp: wwan.update({'shutdown_required': {}}) + # We need to know the amount of other WWAN interfaces as ModemManager needs # to be started or stopped. conf.set_level(base) @@ -57,8 +85,8 @@ def get_config(config=None): get_first_key=True, no_tag_node_value_mangle=True) - # This if-clause is just to be sure - it will always evaluate to true ifname = wwan['ifname'] + # This if-clause is just to be sure - it will always evaluate to true if ifname in wwan['other_interfaces']: del wwan['other_interfaces'][ifname] if len(wwan['other_interfaces']) == 0: @@ -77,18 +105,31 @@ def verify(wwan): verify_interface_exists(ifname) verify_authentication(wwan) verify_vrf(wwan) + verify_mirror_redirect(wwan) return None def generate(wwan): if 'deleted' in wwan: + # We are the last WWAN interface - there are no other ones remaining + # thus the cronjob needs to go away, too + if 'other_interfaces' not in wwan: + if os.path.exists(cron_script): + os.unlink(cron_script) return None + # Install cron triggered helper script to re-dial WWAN interfaces on + # disconnect - e.g. happens during RF signal loss. The script watches every + # WWAN interface - so there is only one instance. if not os.path.exists(cron_script): write_file(cron_script, '*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py') + return None def apply(wwan): + # ModemManager is required to dial WWAN connections - one instance is + # required to serve all modems. Activate ModemManager on first invocation + # of any WWAN interface. if not is_systemd_service_active(service_name): cmd(f'systemctl start {service_name}') @@ -101,17 +142,19 @@ def apply(wwan): break sleep(0.250) - # we only need the modem number. wwan0 -> 0, wwan1 -> 1 - modem = wwan['ifname'].lstrip('wwan') - base_cmd = f'mmcli --modem {modem}' - # Number of bearers is limited - always disconnect first - cmd(f'{base_cmd} --simple-disconnect') + if 'shutdown_required' in wwan: + # we only need the modem number. wwan0 -> 0, wwan1 -> 1 + modem = wwan['ifname'].lstrip('wwan') + base_cmd = f'mmcli --modem {modem}' + # Number of bearers is limited - always disconnect first + cmd(f'{base_cmd} --simple-disconnect') w = WWANIf(wwan['ifname']) if 'deleted' in wwan or 'disable' in wwan: w.remove() - # There are no other WWAN interfaces - stop the daemon + # We are the last WWAN interface - there are no other WWAN interfaces + # remaining, thus we can stop ModemManager and free resources. if 'other_interfaces' not in wwan: cmd(f'systemctl stop {service_name}') # Clean CRON helper script which is used for to re-connect when @@ -121,27 +164,25 @@ def apply(wwan): return None - ip_type = 'ipv4' - slaac = dict_search('ipv6.address.autoconf', wwan) != None - if 'address' in wwan: - if 'dhcp' in wwan['address'] and ('dhcpv6' in wwan['address'] or slaac): - ip_type = 'ipv4v6' - elif 'dhcpv6' in wwan['address'] or slaac: - ip_type = 'ipv6' - elif 'dhcp' in wwan['address']: - ip_type = 'ipv4' - - options = f'ip-type={ip_type},apn=' + wwan['apn'] - if 'authentication' in wwan: - options += ',user={user},password={password}'.format(**wwan['authentication']) - - command = f'{base_cmd} --simple-connect="{options}"' - call(command, stdout=DEVNULL) - w.update(wwan) + if 'shutdown_required' in wwan: + ip_type = 'ipv4' + slaac = dict_search('ipv6.address.autoconf', wwan) != None + if 'address' in wwan: + if 'dhcp' in wwan['address'] and ('dhcpv6' in wwan['address'] or slaac): + ip_type = 'ipv4v6' + elif 'dhcpv6' in wwan['address'] or slaac: + ip_type = 'ipv6' + elif 'dhcp' in wwan['address']: + ip_type = 'ipv4' - if 'other_interfaces' not in wwan and 'deleted' in wwan: - cmd(f'systemctl start {service_name}') + options = f'ip-type={ip_type},apn=' + wwan['apn'] + if 'authentication' in wwan: + options += ',user={user},password={password}'.format(**wwan['authentication']) + command = f'{base_cmd} --simple-connect="{options}"' + call(command, stdout=DEVNULL) + + w.update(wwan) return None if __name__ == '__main__': |