summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-03-28 20:49:16 +0100
committerChristian Poessinger <christian@poessinger.com>2020-03-28 20:49:16 +0100
commitd74f6594be0a6d7697c9deb5a677a82576139b98 (patch)
tree56ba52b263f9f393a789019106259d92d7fe07d0
parent458ccfc2f0fe46d399d2412af60d8ade26b57f65 (diff)
parent722925e159cee6bdaba6f9f2090aedf443c1f032 (diff)
downloadvyos-1x-d74f6594be0a6d7697c9deb5a677a82576139b98.tar.gz
vyos-1x-d74f6594be0a6d7697c9deb5a677a82576139b98.zip
Merge branch 't1988-xml-wirelessmodem' into current
* t1988-xml-wirelessmodem: wwan: T1988: add ipv6 addressing nodes pppoe: T1318: add command to show statistics wwan: T1988: ppp: change order of debug and logfile options wwan: T1988: migrate operational mode commands wwan: T1988: support interface disable wwan: T1988: add support for Sierra Wireless MC7710 modem wwan: T1988: initial XML/Python representation
-rw-r--r--debian/control1
-rw-r--r--debian/vyos-1x.install1
-rw-r--r--interface-definitions/interfaces-wirelessmodem.xml.in80
-rw-r--r--op-mode-definitions/show-interfaces-pppoe.xml9
-rw-r--r--op-mode-definitions/show-interfaces-wirelessmodem.xml45
-rwxr-xr-xsrc/completion/list_wlm_peers.sh6
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py232
-rw-r--r--src/etc/ppp/peers/chat/att6
-rw-r--r--src/etc/ppp/peers/chat/sc113
-rw-r--r--src/etc/ppp/peers/chat/verizon5
-rw-r--r--src/etc/udev/rules.d/99-vyos-wwan.rules11
-rwxr-xr-xsrc/migration-scripts/interfaces/6-to-763
12 files changed, 472 insertions, 0 deletions
diff --git a/debian/control b/debian/control
index 366e8df94..bccfc02d4 100644
--- a/debian/control
+++ b/debian/control
@@ -79,6 +79,7 @@ Depends: python3,
frr,
radvd,
dbus,
+ usb-modeswitch,
hostapd (>= 0.6.8),
wpasupplicant (>= 0.6.7),
iw,
diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index 5bb7ea507..5004d111f 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -3,6 +3,7 @@ etc/init.d
etc/ppp
etc/rsyslog.d
etc/systemd
+etc/udev
etc/vyos
lib/
opt/
diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in
new file mode 100644
index 000000000..cea8f4029
--- /dev/null
+++ b/interface-definitions/interfaces-wirelessmodem.xml.in
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="interfaces">
+ <children>
+ <tagNode name="wirelessmodem" owner="${vyos_conf_scripts_dir}/interfaces-wirelessmodem.py">
+ <properties>
+ <help>Wireless Modem (WWAN) Interface</help>
+ <priority>350</priority>
+ <constraint>
+ <regex>wlm[0-9]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Wireless Modem interface must be named wlmN</constraintErrorMessage>
+ <valueHelp>
+ <format>wlmN</format>
+ <description>Wireless modem interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="apn">
+ <properties>
+ <help>Access Point Name (APN)</help>
+ </properties>
+ </leafNode>
+ <node name="backup">
+ <properties>
+ <help>Insert backup default route</help>
+ </properties>
+ <children>
+ <leafNode name="distance">
+ <properties>
+ <help>Distance backup default route</help>
+ <valueHelp>
+ <format>1-255</format>
+ <description>Distance of the backup route (default: 10) </description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ <constraintErrorMessage>Must be between (1-255)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ #include <include/interface-description.xml.i>
+ #include <include/interface-disable.xml.i>
+ <leafNode name="device">
+ <properties>
+ <help>System device name (default: ttyUSB0)</help>
+ <valueHelp>
+ <format>ttyXXX</format>
+ <description>System TTY device name</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ #include <include/interface-disable-link-detect.xml.i>
+ #include <include/interface-mtu-68-9000.xml.i>
+ <node name="ipv6">
+ <children>
+ #include <include/ipv6-address.xml.i>
+ #include <include/ipv6-disable-forwarding.xml.i>
+ #include <include/ipv6-dup-addr-detect-transmits.xml.i>
+ </children>
+ </node>
+ <leafNode name="no-peer-dns">
+ <properties>
+ <help>Do not use peer supplied DNS server information</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ondemand">
+ <properties>
+ <help>Only dial when traffic is available</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-interfaces-pppoe.xml b/op-mode-definitions/show-interfaces-pppoe.xml
index 3acb14486..591ec8f5b 100644
--- a/op-mode-definitions/show-interfaces-pppoe.xml
+++ b/op-mode-definitions/show-interfaces-pppoe.xml
@@ -27,6 +27,15 @@
</leafNode>
</children>
</node>
+ <leafNode name="statistics">
+ <properties>
+ <help>Show specified wirelessmodem interface statistics</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_pppoe_peers.sh</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/sbin/pppstats $4</command>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/op-mode-definitions/show-interfaces-wirelessmodem.xml b/op-mode-definitions/show-interfaces-wirelessmodem.xml
new file mode 100644
index 000000000..681f54f3d
--- /dev/null
+++ b/op-mode-definitions/show-interfaces-wirelessmodem.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="interfaces">
+ <children>
+ <tagNode name="wirelessmodem">
+ <properties>
+ <help>Show Wireless Modem (WWAN) interface information</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_wlm_peers.sh</script>
+ </completionHelp>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf="$4"</command>
+ <children>
+ <node name="log">
+ <properties>
+ <help>Show PPPoE logs</help>
+ </properties>
+ <command>cat /var/log/vyatta/ppp_$4.log</command>
+ <children>
+ <leafNode name="tail">
+ <properties>
+ <help>Watch PPPoE logs</help>
+ </properties>
+ <command>tail --follow=name /var/log/vyatta/ppp_$4.log</command>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="statistics">
+ <properties>
+ <help>Show specified wirelessmodem interface statistics</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_wlm_peers.sh</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/sbin/pppstats $4</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/src/completion/list_wlm_peers.sh b/src/completion/list_wlm_peers.sh
new file mode 100755
index 000000000..12dd00650
--- /dev/null
+++ b/src/completion/list_wlm_peers.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ -d /etc/ppp/peers ]; then
+ cd /etc/ppp/peers
+ ls wlm*
+fi
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
new file mode 100755
index 000000000..14178d74c
--- /dev/null
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -0,0 +1,232 @@
+#!/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 <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+from copy import deepcopy
+from jinja2 import Template
+from subprocess import Popen, PIPE
+from pwd import getpwnam
+from grp import getgrnam
+
+from vyos.config import Config
+from vyos import ConfigError
+
+# Please be careful if you edit the template.
+config_wwan_tmpl = """### Autogenerated by interfaces-wirelessmodem.py ###
+{% if description %}
+# {{ description }}
+{% endif %}
+
+# physical device
+/dev/{{ device }}
+
+ipparam {{ intf }} {{ metric }}
+ifname {{ intf }}
+linkname {{ intf }}
+{% if on_demand -%}
+demand
+{%- endif %}
+{% if name_server -%}
+usepeerdns
+{%- endif %}
+lcp-echo-failure 0
+115200
+debug
+logfile {{ logfile }}
+nodefaultroute
+ipcp-max-failure 4
+ipcp-accept-local
+ipcp-accept-remote
+noauth
+crtscts
+lock
+persist
+
+connect '/usr/sbin/chat -v -t6 -f {{ chat_script }}'
+
+"""
+
+# Please be careful if you edit the template.
+chat_wwan_tmpl = """
+ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED
+'' AT
+OK ATZ
+OK 'AT+CGDCONT=1,"IP","{{ apn }}"'
+OK ATD*99#
+CONNECT ''
+
+"""
+
+default_config_data = {
+ 'address': [],
+ 'apn': '',
+ 'chat_script': '',
+ 'deleted': False,
+ 'description': '',
+ 'device': 'ttyUSB0',
+ 'disable': False,
+ 'disable_link_detect': 1,
+ 'on_demand': False,
+ 'logfile': '',
+ 'metric': '10',
+ 'mtu': '1500',
+ 'name_server': True,
+ 'intf': ''
+}
+
+def subprocess_cmd(command):
+ p = Popen(command, stdout=PIPE, shell=True)
+ p.communicate()
+
+def check_kmod():
+ modules = ['option', 'usb_wwan', 'usbserial']
+ for module in modules:
+ if not os.path.exists(f'/sys/module/{module}'):
+ if os.system(f'modprobe {module}') != 0:
+ raise ConfigError(f'Loading Kernel module {module} failed')
+
+def get_config():
+ wwan = deepcopy(default_config_data)
+ conf = Config()
+
+ # determine tagNode instance
+ if 'VYOS_TAGNODE_VALUE' not in os.environ:
+ raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
+
+ wwan['intf'] = os.environ['VYOS_TAGNODE_VALUE']
+ wwan['logfile'] = f"/var/log/vyatta/ppp_{wwan['intf']}.log"
+ wwan['chat_script'] = f"/etc/ppp/peers/chat.{wwan['intf']}"
+
+ # Check if interface has been removed
+ if not conf.exists('interfaces wirelessmodem ' + wwan['intf']):
+ wwan['deleted'] = True
+ return wwan
+
+ # set new configuration level
+ conf.set_level('interfaces wirelessmodem ' + wwan['intf'])
+
+ # get metrick for backup default route
+ if conf.exists(['apn']):
+ wwan['apn'] = conf.return_value(['apn'])
+
+ # get metrick for backup default route
+ if conf.exists(['backup', 'distance']):
+ wwan['metric'] = conf.return_value(['backup', 'distance'])
+
+ # Retrieve interface description
+ if conf.exists(['description']):
+ wwan['description'] = conf.return_value(['description'])
+
+ # System device name
+ if conf.exists(['device']):
+ wwan['device'] = conf.return_value(['device'])
+
+ # disable interface
+ if conf.exists('disable'):
+ wwan['disable'] = True
+
+ # ignore link state changes
+ if conf.exists('disable-link-detect'):
+ wwan['disable_link_detect'] = 2
+
+ # Do not use DNS servers provided by the peer
+ if conf.exists(['mtu']):
+ wwan['mtu'] = conf.return_value(['mtu'])
+
+ # Do not use DNS servers provided by the peer
+ if conf.exists(['no-peer-dns']):
+ wwan['name_server'] = False
+
+ # Access concentrator name (only connect to this concentrator)
+ if conf.exists(['ondemand']):
+ wwan['on_demand'] = True
+
+ return wwan
+
+def verify(wwan):
+ if wwan['deleted']:
+ return None
+
+ if not wwan['apn']:
+ raise ConfigError(f"APN for {wwan['intf']} not configured")
+
+ # we can not use isfile() here as Linux device files are no regular files
+ # thus the check will return False
+ if not os.path.exists(f"/dev/{wwan['device']}"):
+ raise ConfigError(f"Device {wwan['device']} does not exist")
+
+ return None
+
+def generate(wwan):
+ config_file_wwan = f"/etc/ppp/peers/{wwan['intf']}"
+
+ # Always hang-up WWAN connection prior generating new configuration file
+ cmd = f"systemctl stop ppp@{wwan['intf']}.service"
+ subprocess_cmd(cmd)
+
+ if wwan['deleted']:
+ # Delete PPP configuration files
+ if os.path.exists(config_file_wwan):
+ os.unlink(config_file_wwan)
+ if os.path.exists(wwan['chat_script']):
+ os.unlink(wwan['chat_script'])
+
+ else:
+ # Create PPP configuration files
+ tmpl = Template(config_wwan_tmpl)
+ config_text = tmpl.render(wwan)
+ with open(config_file_wwan, 'w') as f:
+ f.write(config_text)
+
+
+ # Create PPP chat script
+ tmpl = Template(chat_wwan_tmpl)
+ config_text = tmpl.render(wwan)
+ with open(wwan['chat_script'], 'w') as f:
+ f.write(config_text)
+
+ return None
+
+def apply(wwan):
+ if wwan['deleted']:
+ # bail out early
+ return None
+
+ if not wwan['disable']:
+ # dial WWAN connection
+ cmd = f"systemctl start ppp@{wwan['intf']}.service"
+ subprocess_cmd(cmd)
+
+ # make logfile owned by root / vyattacfg
+ if os.path.isfile(wwan['logfile']):
+ uid = getpwnam('root').pw_uid
+ gid = getgrnam('vyattacfg').gr_gid
+ os.chown(wwan['logfile'], uid, gid)
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ check_kmod()
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/etc/ppp/peers/chat/att b/src/etc/ppp/peers/chat/att
new file mode 100644
index 000000000..7a02682f5
--- /dev/null
+++ b/src/etc/ppp/peers/chat/att
@@ -0,0 +1,6 @@
+ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED
+'' AT
+OK ATZ
+OK 'AT+CGDCONT=1,"IP","ISP.CINGULAR"'
+OK ATD*99#
+CONNECT ''
diff --git a/src/etc/ppp/peers/chat/sc1 b/src/etc/ppp/peers/chat/sc1
new file mode 100644
index 000000000..fbfabd8c2
--- /dev/null
+++ b/src/etc/ppp/peers/chat/sc1
@@ -0,0 +1,13 @@
+TIMEOUT 60
+ABORT ERROR
+ABORT BUSY
+ABORT VOICE
+ABORT "NO CARRIER"
+ABORT "NO DIALTONE"
+ABORT "NO DIAL TONE"
+ABORT "NO ANSWER"
+"" "ATZ"
+"" "AT&FH0M0"
+OK-AT-OK "ATDT*99#"
+TIMEOUT 75
+CONNECT
diff --git a/src/etc/ppp/peers/chat/verizon b/src/etc/ppp/peers/chat/verizon
new file mode 100644
index 000000000..a36a3e915
--- /dev/null
+++ b/src/etc/ppp/peers/chat/verizon
@@ -0,0 +1,5 @@
+ABORT 'NO CARRIER' ABORT 'ERROR' ABORT 'NO DIALTONE' ABORT
+'BUSY' ABORT 'NO ANSWER'
+'' ATZ
+OK-AT-OK ATDT#777
+CONNECT \d\c
diff --git a/src/etc/udev/rules.d/99-vyos-wwan.rules b/src/etc/udev/rules.d/99-vyos-wwan.rules
new file mode 100644
index 000000000..67f30a3dd
--- /dev/null
+++ b/src/etc/udev/rules.d/99-vyos-wwan.rules
@@ -0,0 +1,11 @@
+ACTION!="add|change", GOTO="mbim_to_qmi_rules_end"
+
+SUBSYSTEM!="usb", GOTO="mbim_to_qmi_rules_end"
+
+# ignore any device with only one configuration
+ATTR{bNumConfigurations}=="1", GOTO="mbim_to_qmi_rules_end"
+
+# force Sierra Wireless MC7710 to configuration #1
+ATTR{idVendor}=="1199",ATTR{idProduct}=="68a2",ATTR{bConfigurationValue}="1"
+
+LABEL="mbim_to_qmi_rules_end"
diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7
new file mode 100755
index 000000000..b4f59c363
--- /dev/null
+++ b/src/migration-scripts/interfaces/6-to-7
@@ -0,0 +1,63 @@
+#!/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 <http://www.gnu.org/licenses/>.
+
+# Remove network provider name from CLI and rather use provider APN from CLI
+
+import sys
+from vyos.configtree import ConfigTree
+
+if __name__ == '__main__':
+ if (len(sys.argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+ file_name = sys.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
+ sys.exit(0)
+
+ # list all individual interface types like dummy, ethernet and so on
+ for i in config.list_nodes(base):
+ iface = base + [i]
+
+ # only three carries have been supported in the past, thus
+ # this will be fairly simple \o/ - and only one (AT&T) did
+ # configure an APN
+ if config.exists(iface + ['network']):
+ network = config.return_value(iface + ['network'])
+ if network == "att":
+ apn = 'isp.cingular'
+ config.set(iface + ['apn'], value=apn)
+
+ config.delete(iface + ['network'])
+
+ # synchronize DNS configuration with PPPoE interfaces to have a
+ # uniform CLI experience
+ if config.exists(iface + ['no-dns']):
+ config.rename(iface + ['no-dns'], 'no-peer-dns')
+
+ 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))
+ sys.exit(1)