From 8a98235b5d7f19a0f90cbd536097b3aef0ea29d7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 12 Jun 2021 13:17:13 +0200 Subject: wwan: T3620: rename "wirelessmodem wlm" interfaces to new wwan interface tree (cherry picked from commit c2a1c071e7d0a9ca754d7f5016eed7db188b3d1a) --- data/configd-include.json | 2 +- data/templates/wwan/chat.tmpl | 10 -- data/templates/wwan/ip-down.script.tmpl | 27 ----- data/templates/wwan/ip-pre-up.script.tmpl | 23 ---- data/templates/wwan/ip-up.script.tmpl | 25 ---- data/templates/wwan/peer.tmpl | 31 ----- debian/control | 1 + interface-definitions/interfaces-ethernet.xml.in | 8 +- .../interfaces-wirelessmodem.xml.in | 83 ------------- interface-definitions/interfaces-wwan.xml.in | 45 +++++++ op-mode-definitions/connect.xml.in | 2 +- op-mode-definitions/disconnect.xml.in | 2 +- .../show-interfaces-wirelessmodem.xml.in | 51 -------- op-mode-definitions/show-interfaces-wwan.xml.in | 103 ++++++++++++++++ python/vyos/ifconfig/__init__.py | 1 + python/vyos/ifconfig/wwan.py | 28 +++++ python/vyos/xml/test_xml.py | 8 +- .../scripts/cli/test_interfaces_wirelessmodem.py | 82 ------------- src/conf_mode/interfaces-wirelessmodem.py | 132 --------------------- src/conf_mode/interfaces-wwan.py | 86 ++++++++++++++ .../system/ModemManager.service.d/override.conf | 7 ++ src/migration-scripts/interfaces/18-to-19 | 79 ++++++++++++ src/op_mode/show_wwan.py | 78 ++++++++++++ src/validators/interface-name | 2 +- src/validators/vrf-name | 2 +- 25 files changed, 441 insertions(+), 477 deletions(-) delete mode 100644 data/templates/wwan/chat.tmpl delete mode 100644 data/templates/wwan/ip-down.script.tmpl delete mode 100644 data/templates/wwan/ip-pre-up.script.tmpl delete mode 100644 data/templates/wwan/ip-up.script.tmpl delete mode 100644 data/templates/wwan/peer.tmpl delete mode 100644 interface-definitions/interfaces-wirelessmodem.xml.in create mode 100644 interface-definitions/interfaces-wwan.xml.in delete mode 100644 op-mode-definitions/show-interfaces-wirelessmodem.xml.in create mode 100644 op-mode-definitions/show-interfaces-wwan.xml.in create mode 100644 python/vyos/ifconfig/wwan.py delete mode 100755 smoketest/scripts/cli/test_interfaces_wirelessmodem.py delete mode 100755 src/conf_mode/interfaces-wirelessmodem.py create mode 100755 src/conf_mode/interfaces-wwan.py create mode 100644 src/etc/systemd/system/ModemManager.service.d/override.conf create mode 100755 src/migration-scripts/interfaces/18-to-19 create mode 100755 src/op_mode/show_wwan.py diff --git a/data/configd-include.json b/data/configd-include.json index eb1dd13f9..7501a6264 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -24,7 +24,7 @@ "interfaces-vxlan.py", "interfaces-wireguard.py", "interfaces-wireless.py", -"interfaces-wirelessmodem.py", +"interfaces-wwan.py", "ipsec-settings.py", "lldp.py", "nat.py", diff --git a/data/templates/wwan/chat.tmpl b/data/templates/wwan/chat.tmpl deleted file mode 100644 index 386af37e6..000000000 --- a/data/templates/wwan/chat.tmpl +++ /dev/null @@ -1,10 +0,0 @@ -ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED -'' AT -OK ATZ -{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %} -OK 'AT+CGDCONT=1,"IPV4V6","{{ apn }}"' -{% else %} -OK 'AT+CGDCONT=1,"IP","{{ apn }}"' -{% endif %} -OK ATD*99# -CONNECT '' diff --git a/data/templates/wwan/ip-down.script.tmpl b/data/templates/wwan/ip-down.script.tmpl deleted file mode 100644 index 9dc15ea99..000000000 --- a/data/templates/wwan/ip-down.script.tmpl +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -# Script parameters will be like: -# wlm0 /dev/serial/by-bus/usb0b1.3p1.3 115200 10.100.118.91 10.64.64.64 wlm0 - -# Only applicable for Wireless Modems (WWAN) -if [ -z $(echo $2 | egrep "(ttyS[0-9]+|usb[0-9]+b.*)$") ]; then - exit 0 -fi - -# Determine if we are running inside a VRF or not, required for proper routing table -# NOTE: the down script can not be properly templated as we need the VRF name, -# which is not present on deletion, thus we read it from the operating system. -if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then - # Determine upper (VRF) interface - VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*)) - # Remove upper_ prefix from result string - VRF_NAME=${VRF#"upper_"} - # Remove default route from VRF routing table - vtysh -c "conf t" -c "vrf ${VRF_NAME}" -c "no ip route 0.0.0.0/0 {{ ifname }}" -else - # Remove default route from GRT (global routing table) - vtysh -c "conf t" -c "no ip route 0.0.0.0/0 {{ ifname }}" -fi - -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "removed default route via {{ ifname }} metric {{ backup.distance }}" diff --git a/data/templates/wwan/ip-pre-up.script.tmpl b/data/templates/wwan/ip-pre-up.script.tmpl deleted file mode 100644 index 199150947..000000000 --- a/data/templates/wwan/ip-pre-up.script.tmpl +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# As WWAN is an "on demand" interface we need to re-configure it when it -# becomes 'up' - -ipparam=$6 - -# device name and metric are received using ipparam -device=`echo "$ipparam"|awk '{ print $1 }'` - -if [ "$device" != "{{ ifname }}" ]; then - exit -fi - -# add some info to syslog -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "executing $0" - -echo "{{ description }}" > /sys/class/net/{{ ifname }}/ifalias - -{% if vrf %} -logger -t pppd[$DIALER_PID] "configuring interface {{ ifname }} for VRF {{ vrf }}" -ip link set dev {{ ifname }} master {{ vrf }} -{% endif %} diff --git a/data/templates/wwan/ip-up.script.tmpl b/data/templates/wwan/ip-up.script.tmpl deleted file mode 100644 index 2603a0286..000000000 --- a/data/templates/wwan/ip-up.script.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -# Script parameters will be like: -# wlm0 /dev/serial/by-bus/usb0b1.3p1.3 115200 10.100.118.91 10.64.64.64 wlm0 - -# Only applicable for Wireless Modems (WWAN) -if [ -z $(echo $2 | egrep "(ttyS[0-9]+|usb[0-9]+b.*)$") ]; then - exit 0 -fi - -# Determine if we are running inside a VRF or not, required for proper routing table -if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then - # Determine upper (VRF) interface - VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*)) - # Remove upper_ prefix from result string - VRF_NAME=${VRF#"upper_"} - # Remove default route from VRF routing table - vtysh -c "conf t" -c "vrf ${VRF_NAME}" -c "ip route 0.0.0.0/0 {{ ifname }} {{ backup.distance }}" -else - # Remove default route from GRT (global routing table) - vtysh -c "conf t" -c "ip route 0.0.0.0/0 {{ ifname }} {{ backup.distance }}" -fi - -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "added default route via {{ ifname }} metric {{ backup.distance }} ${VRF_NAME}" diff --git a/data/templates/wwan/peer.tmpl b/data/templates/wwan/peer.tmpl deleted file mode 100644 index 2807a79a4..000000000 --- a/data/templates/wwan/peer.tmpl +++ /dev/null @@ -1,31 +0,0 @@ -### Autogenerated by interfaces-wirelessmodem.py ### - -{{ "# description: " + description if description is defined }} -ifname {{ ifname }} -ipparam {{ ifname }} -linkname {{ ifname }} - -{{ "usepeerdns" if no_peer_dns is defined }} -# physical device -{{ device }} -lcp-echo-failure 0 -115200 -debug -mtu {{ mtu }} -mru {{ mtu }} -{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %} -+ipv6 -ipv6cp-use-ipaddr -{% endif %} -nodefaultroute -ipcp-max-failure 4 -ipcp-accept-local -ipcp-accept-remote -noauth -crtscts -lock -persist -{{ "demand" if connect_on_demand is defined }} - -connect '/usr/sbin/chat -v -t6 -f /etc/ppp/peers/chat.{{ ifname }}' - diff --git a/debian/control b/debian/control index 6a04c8b38..02586928f 100644 --- a/debian/control +++ b/debian/control @@ -72,6 +72,7 @@ Depends: lsscsi, mdns-repeater, minisign, + modemmanager, mtr-tiny, netplug, nftables (>= 0.9.3), diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index 54be3a43b..b13072404 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -6,14 +6,14 @@ Ethernet Interface 318 - - ^((eth|lan)[0-9]+|(eno|ens|enp|enx).+)$ - - Invalid Ethernet interface name ethN Ethernet interface name + + ^((eth|lan)[0-9]+|(eno|ens|enp|enx).+)$ + + Invalid Ethernet interface name #include diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in deleted file mode 100644 index 25ac2d6e0..000000000 --- a/interface-definitions/interfaces-wirelessmodem.xml.in +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - Wireless Modem (WWAN) Interface - 350 - - ^wlm[0-9]+$ - - Wireless Modem interface must be named wlmN - - wlmN - Wireless modem interface name - - - - - - Access Point Name (APN) - - - - - Insert backup default route - - - - - Distance backup default route - - 1-255 - Distance of the backup route (default: 10) - - - - - Must be between (1-255) - - 10 - - - - #include - #include - #include - - - Serial device - - - - - - ttySXX - TTY device name, regular serial port - - - usbNbXpY - TTY device name, USB based - - - ^(ttyS[0-9]+|usb[0-9]+b.*)$ - - - - #include - #include - #include - #include - - - Do not use peer supplied DNS server information - - - - #include - - - - - diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in new file mode 100644 index 000000000..5afdedf9b --- /dev/null +++ b/interface-definitions/interfaces-wwan.xml.in @@ -0,0 +1,45 @@ + + + + + + + Wireless Modem (WWAN) Interface + 350 + + + + + ^wwan[0-9]+$ + + Wireless Modem interface must be named wwanN + + wwanN + Wireless Wide Area Network interface name + + + + #include + + + Access Point Name (APN) + + + #include + #include + #include + #include + #include + #include + #include + + 1430 + + #include + #include + #include + + + + + diff --git a/op-mode-definitions/connect.xml.in b/op-mode-definitions/connect.xml.in index 1ec62949a..8f19eac70 100644 --- a/op-mode-definitions/connect.xml.in +++ b/op-mode-definitions/connect.xml.in @@ -19,7 +19,7 @@ Bring up a connection-oriented network interface interfaces pppoe - interfaces wirelessmodem + interfaces wwan sudo ${vyos_op_scripts_dir}/connect_disconnect.py --connect "$3" diff --git a/op-mode-definitions/disconnect.xml.in b/op-mode-definitions/disconnect.xml.in index bf2c37b89..4415c0ed2 100644 --- a/op-mode-definitions/disconnect.xml.in +++ b/op-mode-definitions/disconnect.xml.in @@ -10,7 +10,7 @@ Take down a connection-oriented network interface interfaces pppoe - interfaces wirelessmodem + interfaces wwan sudo ${vyos_op_scripts_dir}/connect_disconnect.py --disconnect "$3" diff --git a/op-mode-definitions/show-interfaces-wirelessmodem.xml.in b/op-mode-definitions/show-interfaces-wirelessmodem.xml.in deleted file mode 100644 index 18b1e55c7..000000000 --- a/op-mode-definitions/show-interfaces-wirelessmodem.xml.in +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - Show Wireless Modem (WWAN) interface information - - interfaces wirelessmodem - - - ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" - - - - Show specified WWAN interface log - - /usr/bin/journalctl --unit "ppp@$4".service - - - - Show specified WWAN interface statistics - - interfaces wirelessmodem - - - if [ -d "/sys/class/net/$4" ]; then /usr/sbin/pppstats "$4"; fi - - - - - - Show Wireless Modem (WWAN) interface information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wirelessmodem --action=show-brief - - - - Show detailed Wireless Modem (WWAN( interface information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wirelessmodem --action=show - - - - - - - - diff --git a/op-mode-definitions/show-interfaces-wwan.xml.in b/op-mode-definitions/show-interfaces-wwan.xml.in new file mode 100644 index 000000000..d57e17a13 --- /dev/null +++ b/op-mode-definitions/show-interfaces-wwan.xml.in @@ -0,0 +1,103 @@ + + + + + + + + + Show Wireless Wire Area Network (WWAN) interface information + + interfaces wwan + + + + ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" + + + + Show WWAN module capabilities + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --capabilities + + + + Show WWAN module firmware + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --firmware + + + + Show WWAN module IMEI/ESN/MEID + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --imei + + + + Show WWAN module IMSI + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --imsi + + + + Show WWAN module manufacturer + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --model + + + + Show WWAN module MSISDN + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --msisdn + + + + Show WWAN module revision + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --revision + + + + Show WWAN module RF signal info + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --signal + + + + Show WWAN module connected SIM card information + + sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --sim + + + + Show WWAN module information summary + + mmcli --modem ${4#wwan} + + + + Show interface log for specified interface + + echo not implemented + + + + + + Show Wireless Modem (WWAN) interface information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wirelessmodem --action=show-brief + + + + Show detailed Wireless Modem (WWAN( interface information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wirelessmodem --action=show + + + + + + + + diff --git a/python/vyos/ifconfig/__init__.py b/python/vyos/ifconfig/__init__.py index 9cd8d44c1..d3db4dbad 100644 --- a/python/vyos/ifconfig/__init__.py +++ b/python/vyos/ifconfig/__init__.py @@ -42,3 +42,4 @@ from vyos.ifconfig.tunnel import Sit6RDIf from vyos.ifconfig.wireless import WiFiIf from vyos.ifconfig.l2tpv3 import L2TPv3If from vyos.ifconfig.macsec import MACsecIf +from vyos.ifconfig.wwan import WWANIf diff --git a/python/vyos/ifconfig/wwan.py b/python/vyos/ifconfig/wwan.py new file mode 100644 index 000000000..f18959a60 --- /dev/null +++ b/python/vyos/ifconfig/wwan.py @@ -0,0 +1,28 @@ +# Copyright 2021 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +from vyos.ifconfig.interface import Interface + +@Interface.register +class WWANIf(Interface): + iftype = 'wwan' + definition = { + **Interface.definition, + **{ + 'section': 'wwan', + 'prefixes': ['wwan', ], + 'eternal': 'wwan[0-9]+$', + }, + } diff --git a/python/vyos/xml/test_xml.py b/python/vyos/xml/test_xml.py index ff55151d2..3a6f0132d 100644 --- a/python/vyos/xml/test_xml.py +++ b/python/vyos/xml/test_xml.py @@ -59,7 +59,7 @@ class TestSearch(TestCase): last = self.xml.traverse("interfaces") self.assertEqual(last, '') self.assertEqual(self.xml.inside, ['interfaces']) - self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wirelessmodem']) + self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wwan']) self.assertEqual(self.xml.filling, False) self.assertEqual(self.xml.word, '') self.assertEqual(self.xml.check, False) @@ -72,7 +72,7 @@ class TestSearch(TestCase): last = self.xml.traverse("interfaces ") self.assertEqual(last, '') self.assertEqual(self.xml.inside, ['interfaces']) - self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wirelessmodem']) + self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wwan']) self.assertEqual(self.xml.filling, False) self.assertEqual(self.xml.word, last) self.assertEqual(self.xml.check, False) @@ -85,7 +85,7 @@ class TestSearch(TestCase): last = self.xml.traverse("interfaces w") self.assertEqual(last, 'w') self.assertEqual(self.xml.inside, ['interfaces']) - self.assertEqual(self.xml.options, ['wireguard', 'wireless', 'wirelessmodem']) + self.assertEqual(self.xml.options, ['wireguard', 'wireless', 'wwan']) self.assertEqual(self.xml.filling, True) self.assertEqual(self.xml.word, last) self.assertEqual(self.xml.check, True) @@ -276,4 +276,4 @@ class TestSearch(TestCase): self.assertEqual(self.xml.filled, True) self.assertEqual(self.xml.plain, False) - # Need to add a check for a valuless leafNode \ No newline at end of file + # Need to add a check for a valuless leafNode diff --git a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py deleted file mode 100755 index 023f57305..000000000 --- a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020-2021 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 -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import unittest - -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError - -config_file = '/etc/ppp/peers/{}' -base_path = ['interfaces', 'wirelessmodem'] - -def get_config_value(interface, key): - with open(config_file.format(interface), 'r') as f: - for line in f: - if line.startswith(key): - return list(line.split()) - return [] - -class WWANInterfaceTest(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - self._interfaces = ['wlm0', 'wlm1'] - - def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session - - def test_wwan(self): - for interface in self._interfaces: - self.session.set(base_path + [interface, 'no-peer-dns']) - self.session.set(base_path + [interface, 'connect-on-demand']) - - # check validate() - APN must be configure - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + [interface, 'apn', 'vyos.net']) - - # check validate() - device must be configure - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + [interface, 'device', 'ttyS0']) - - # commit changes - self.session.commit() - - # verify configuration file(s) - for interface in self._interfaces: - tmp = get_config_value(interface, 'ifname')[1] - self.assertTrue(interface in tmp) - - tmp = get_config_value(interface, 'demand')[0] - self.assertTrue('demand' in tmp) - - tmp = os.path.isfile(f'/etc/ppp/peers/chat.{interface}') - self.assertTrue(tmp) - - # Check if ppp process is running in the interface in question - running = False - for p in process_iter(): - if "pppd" in p.name(): - if interface in p.cmdline(): - running = True - - self.assertTrue(running) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py deleted file mode 100755 index 976953b31..000000000 --- a/src/conf_mode/interfaces-wirelessmodem.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 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 -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os - -from sys import exit - -from vyos.config import Config -from vyos.configdict import get_interface_dict -from vyos.configverify import verify_vrf -from vyos.template import render -from vyos.util import call -from vyos.util import check_kmod -from vyos.util import find_device_file -from vyos import ConfigError -from vyos import airbag -airbag.enable() - -k_mod = ['option', 'usb_wwan', 'usbserial'] - -def get_config(config=None): - """ - Retrive CLI config as dictionary. Dictionary can never be empty, as at least the - interface name will be added or a deleted flag - """ - if config: - conf = config - else: - conf = Config() - base = ['interfaces', 'wirelessmodem'] - wwan = get_interface_dict(conf, base) - - return wwan - -def verify(wwan): - if 'deleted' in wwan: - return None - - if not 'apn' in wwan: - raise ConfigError('No APN configured for "{ifname}"'.format(**wwan)) - - if not 'device' in wwan: - raise ConfigError('Physical "device" must be configured') - - # we can not use isfile() here as Linux device files are no regular files - # thus the check will return False - dev_path = find_device_file(wwan['device']) - if dev_path is None or not os.path.exists(dev_path): - raise ConfigError('Device "{device}" does not exist'.format(**wwan)) - - verify_vrf(wwan) - - return None - -def generate(wwan): - # set up configuration file path variables where our templates will be - # rendered into - ifname = wwan['ifname'] - config_wwan = f'/etc/ppp/peers/{ifname}' - config_wwan_chat = f'/etc/ppp/peers/chat.{ifname}' - script_wwan_pre_up = f'/etc/ppp/ip-pre-up.d/1010-vyos-wwan-{ifname}' - script_wwan_ip_up = f'/etc/ppp/ip-up.d/1010-vyos-wwan-{ifname}' - script_wwan_ip_down = f'/etc/ppp/ip-down.d/1010-vyos-wwan-{ifname}' - - config_files = [config_wwan, config_wwan_chat, script_wwan_pre_up, - script_wwan_ip_up, script_wwan_ip_down] - - # Always hang-up WWAN connection prior generating new configuration file - call(f'systemctl stop ppp@{ifname}.service') - - if 'deleted' in wwan: - # Delete PPP configuration files - for file in config_files: - if os.path.exists(file): - os.unlink(file) - - else: - wwan['device'] = find_device_file(wwan['device']) - - # Create PPP configuration files - render(config_wwan, 'wwan/peer.tmpl', wwan) - # Create PPP chat script - render(config_wwan_chat, 'wwan/chat.tmpl', wwan) - - # generated script file must be executable - - # Create script for ip-pre-up.d - render(script_wwan_pre_up, 'wwan/ip-pre-up.script.tmpl', - wwan, permission=0o755) - # Create script for ip-up.d - render(script_wwan_ip_up, 'wwan/ip-up.script.tmpl', - wwan, permission=0o755) - # Create script for ip-down.d - render(script_wwan_ip_down, 'wwan/ip-down.script.tmpl', - wwan, permission=0o755) - - return None - -def apply(wwan): - if 'deleted' in wwan: - # bail out early - return None - - if not 'disable' in wwan: - # "dial" WWAN connection - call('systemctl start ppp@{ifname}.service'.format(**wwan)) - - return None - -if __name__ == '__main__': - try: - check_kmod(k_mod) - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py new file mode 100755 index 000000000..02d2c723d --- /dev/null +++ b/src/conf_mode/interfaces-wwan.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020-2021 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 +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import get_interface_dict +from vyos.configverify import verify_interface_exists +from vyos.configverify import verify_vrf +from vyos.ifconfig import WWANIf +from vyos.util import cmd +from vyos.template import render +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +def get_config(config=None): + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ + if config: + conf = config + else: + conf = Config() + base = ['interfaces', 'wwan'] + wwan = get_interface_dict(conf, base) + + return wwan + +def verify(wwan): + if 'deleted' in wwan: + return None + + ifname = wwan['ifname'] + if not 'apn' in wwan: + raise ConfigError(f'No APN configured for "{ifname}"!') + + verify_interface_exists(ifname) + verify_vrf(wwan) + + return None + +def generate(wwan): + return None + +def apply(wwan): + # we only need the modem number. wwan0 -> 0, wwan1 -> 1 + modem = wwan['ifname'].replace('wwan','') + base_cmd = f'mmcli --modem {modem}' + + w = WWANIf(wwan['ifname']) + if 'deleted' in wwan or 'disable' in wwan: + w.remove() + cmd(f'{base_cmd} --simple-disconnect') + return None + + cmd(f'{base_cmd} --simple-connect=\"apn={wwan["apn"]}\"') + w.update(wwan) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/etc/systemd/system/ModemManager.service.d/override.conf b/src/etc/systemd/system/ModemManager.service.d/override.conf new file mode 100644 index 000000000..07a18460e --- /dev/null +++ b/src/etc/systemd/system/ModemManager.service.d/override.conf @@ -0,0 +1,7 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/ModemManager --filter-policy=strict --log-level=INFO --log-timestamps --log-journal diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19 new file mode 100755 index 000000000..03b990afb --- /dev/null +++ b/src/migration-scripts/interfaces/18-to-19 @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + base = ['interfaces', 'wirelessmodem'] + if not config.exists(base): + # Nothing to do + exit(0) + + new_base = ['interfaces', 'wwan'] + config.set(new_base) + config.set_tag(new_base) + for interface in config.list_nodes(base): + # convert usb0b1.3p1.2 device identifier and extract 1.3 usb bus id + usb = config.return_value(base + [interface, 'device']) + device = usb.split('b')[-1] + busid = device.split('p')[0] + for wwan in os.listdir('/sys/class/net'): + # we are only interested in interfaces starting with wwan + if not wwan.startswith('wwan'): + continue + device = os.readlink(f'/sys/class/net/{wwan}/device') + device = device.split(':')[0] + if busid in device: + config.copy(base + [interface], new_base + [wwan]) + + config.delete(base) + + # Now that we have copied the old wirelessmodem interfaces to wwan + # we can start to migrate also individual config items. + for interface in config.list_nodes(new_base): + # we do no longer need the USB device name + config.delete(new_base + [interface, 'device']) + # set/unset DNS configuration + dns = new_base + [interface, 'no-peer-dns'] + if config.exists(dns): + config.delete(dns) + else: + config.set(['system', 'name-servers-dhcp'], value=interface, replace=False) + + # the new wwan interface use regular IP addressing + config.set(new_base + [interface, 'address'], value='dhcp') + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/op_mode/show_wwan.py b/src/op_mode/show_wwan.py new file mode 100755 index 000000000..249dda2a5 --- /dev/null +++ b/src/op_mode/show_wwan.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import argparse + +from sys import exit +from vyos.util import cmd + +parser = argparse.ArgumentParser() +parser.add_argument("--model", help="Get module model", action="store_true") +parser.add_argument("--revision", help="Get module revision", action="store_true") +parser.add_argument("--capabilities", help="Get module capabilities", action="store_true") +parser.add_argument("--imei", help="Get module IMEI/ESN/MEID", action="store_true") +parser.add_argument("--imsi", help="Get module IMSI", action="store_true") +parser.add_argument("--msisdn", help="Get module MSISDN", action="store_true") +parser.add_argument("--sim", help="Get SIM card status", action="store_true") +parser.add_argument("--signal", help="Get current RF signal info", action="store_true") +parser.add_argument("--firmware", help="Get current RF signal info", action="store_true") + +required = parser.add_argument_group('Required arguments') +required.add_argument("--interface", help="WWAN interface name, e.g. wwan0", required=True) + +def qmi_cmd(device, command, silent=False): + tmp = cmd(f'qmicli --device={device} --device-open-proxy {command}') + tmp = tmp.replace(f'[{cdc}] ', '') + if not silent: + # skip first line as this only holds the info headline + for line in tmp.splitlines()[1:]: + print(line.lstrip()) + return tmp + +if __name__ == '__main__': + args = parser.parse_args() + + # remove the WWAN prefix from the interface, required for the CDC interface + if_num = args.interface.replace('wwan','') + cdc = f'/dev/cdc-wdm{if_num}' + + if args.model: + qmi_cmd(cdc, '--dms-get-model') + elif args.capabilities: + qmi_cmd(cdc, '--dms-get-capabilities') + qmi_cmd(cdc, '--dms-get-band-capabilities') + elif args.revision: + qmi_cmd(cdc, '--dms-get-revision') + elif args.imei: + qmi_cmd(cdc, '--dms-get-ids') + elif args.imsi: + qmi_cmd(cdc, '--dms-uim-get-imsi') + elif args.msisdn: + qmi_cmd(cdc, '--dms-get-msisdn') + elif args.sim: + qmi_cmd(cdc, '--uim-get-card-status') + elif args.signal: + qmi_cmd(cdc, '--nas-get-signal-info') + qmi_cmd(cdc, '--nas-get-rf-band-info') + elif args.firmware: + tmp = qmi_cmd(cdc, '--dms-get-manufacturer', silent=True) + if 'Sierra Wireless' in tmp: + qmi_cmd(cdc, '--dms-swi-get-current-firmware') + else: + qmi_cmd(cdc, '--dms-get-software-version') + else: + parser.print_help() + exit(1) diff --git a/src/validators/interface-name b/src/validators/interface-name index 5bac671b1..105815eee 100755 --- a/src/validators/interface-name +++ b/src/validators/interface-name @@ -20,7 +20,7 @@ import re from sys import argv from sys import exit -pattern = '^(bond|br|dum|en|ersp|eth|gnv|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|vti|vtun|vxlan|wg|wlan|wlm)[0-9]+(.\d+)?|lo$' +pattern = '^(bond|br|dum|en|ersp|eth|gnv|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|vti|vtun|vxlan|wg|wlan|wwan)[0-9]+(.\d+)?|lo$' if __name__ == '__main__': if len(argv) != 2: diff --git a/src/validators/vrf-name b/src/validators/vrf-name index 7b6313888..c78a80776 100755 --- a/src/validators/vrf-name +++ b/src/validators/vrf-name @@ -34,7 +34,7 @@ if __name__ == '__main__': exit(1) pattern = "^(?!(bond|br|dum|eth|lan|eno|ens|enp|enx|gnv|ipoe|l2tp|l2tpeth|" \ - "vtun|ppp|pppoe|peth|tun|vti|vxlan|wg|wlan|wlm)\d+(\.\d+(v.+)?)?$).*$" + "vtun|ppp|pppoe|peth|tun|vti|vxlan|wg|wlan|wwan)\d+(\.\d+(v.+)?)?$).*$" if not re.match(pattern, vrf): exit(1) -- cgit v1.2.3