summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/interfaces-bonding.xml.in2
-rw-r--r--interface-definitions/interfaces-bridge.xml.in4
-rw-r--r--interface-definitions/interfaces-dummy.xml.in2
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in2
-rw-r--r--interface-definitions/interfaces-geneve.xml.in2
-rw-r--r--interface-definitions/interfaces-l2tpv3.xml.in4
-rw-r--r--interface-definitions/interfaces-loopback.xml.in2
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in2
-rw-r--r--interface-definitions/interfaces-pppoe.xml.in2
-rw-r--r--interface-definitions/interfaces-pseudo-ethernet.xml.in2
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in2
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in2
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in2
-rw-r--r--interface-definitions/interfaces-wireless.xml.in2
-rw-r--r--interface-definitions/interfaces-wirelessmodem.xml.in3
-rw-r--r--interface-definitions/vrf.xml.in6
-rw-r--r--python/vyos/ifconfig/__init__.py1
-rw-r--r--python/vyos/ifconfig/l2tpv3.py30
-rw-r--r--python/vyos/util.py57
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py82
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py9
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py88
-rwxr-xr-xsrc/migration-scripts/quagga/5-to-663
-rwxr-xr-xsrc/validators/mac-address18
-rwxr-xr-xsrc/validators/vrf-name40
25 files changed, 296 insertions, 133 deletions
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 166f23285..07a6abd30 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -7,7 +7,7 @@
<help>Bonding Interface/Link Aggregation</help>
<priority>320</priority>
<constraint>
- <regex>bond[0-9]+$</regex>
+ <regex>^bond[0-9]+$</regex>
</constraint>
<constraintErrorMessage>Bonding interface must be named bondN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index f41110a3f..818bc9c0e 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -5,9 +5,9 @@
<tagNode name="bridge" owner="${vyos_conf_scripts_dir}/interfaces-bridge.py">
<properties>
<help>Bridge Interface</help>
- <priority>470</priority>
+ <priority>489</priority>
<constraint>
- <regex>br[0-9]+$</regex>
+ <regex>^br[0-9]+$</regex>
</constraint>
<constraintErrorMessage>Bridge interface must be named brN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in
index 5229e602a..135adfc10 100644
--- a/interface-definitions/interfaces-dummy.xml.in
+++ b/interface-definitions/interfaces-dummy.xml.in
@@ -7,7 +7,7 @@
<help>Dummy Interface</help>
<priority>300</priority>
<constraint>
- <regex>dum[0-9]+$</regex>
+ <regex>^dum[0-9]+$</regex>
</constraint>
<constraintErrorMessage>Dummy interface must be named dumN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 2b461cfaa..f8ec26d04 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -7,7 +7,7 @@
<help>Ethernet Interface</help>
<priority>318</priority>
<constraint>
- <regex>((eth|lan)[0-9]+|(eno|ens|enp|enx).+)$</regex>
+ <regex>^((eth|lan)[0-9]+|(eno|ens|enp|enx).+)$</regex>
</constraint>
<constraintErrorMessage>Invalid Ethernet interface name</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index a6406ffc9..31a3ebb7a 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -7,7 +7,7 @@
<help>Generic Network Virtualization Encapsulation (GENEVE) Interface</help>
<priority>460</priority>
<constraint>
- <regex>gnv[0-9]+$</regex>
+ <regex>^gnv[0-9]+$</regex>
</constraint>
<constraintErrorMessage>GENEVE interface must be named gnvN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in
index 161a37df8..30dd9b604 100644
--- a/interface-definitions/interfaces-l2tpv3.xml.in
+++ b/interface-definitions/interfaces-l2tpv3.xml.in
@@ -5,9 +5,9 @@
<tagNode name="l2tpv3" owner="${vyos_conf_scripts_dir}/interfaces-l2tpv3.py">
<properties>
<help>Layer 2 Tunnel Protocol Version 3 (L2TPv3) Interface</help>
- <priority>800</priority>
+ <priority>485</priority>
<constraint>
- <regex>l2tpeth[0-9]+$</regex>
+ <regex>^l2tpeth[0-9]+$</regex>
</constraint>
<constraintErrorMessage>L2TPv3 interface must be named l2tpethN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in
index ddbfad763..97d5bab90 100644
--- a/interface-definitions/interfaces-loopback.xml.in
+++ b/interface-definitions/interfaces-loopback.xml.in
@@ -7,7 +7,7 @@
<help>Loopback Interface</help>
<priority>300</priority>
<constraint>
- <regex>lo$</regex>
+ <regex>^lo$</regex>
</constraint>
<constraintErrorMessage>Loopback interface must be named lo</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index cd5b5f29e..92bac3fab 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -7,7 +7,7 @@
<help>OpenVPN Tunnel Interface</help>
<priority>460</priority>
<constraint>
- <regex>vtun[0-9]+$</regex>
+ <regex>^vtun[0-9]+$</regex>
</constraint>
<constraintErrorMessage>OpenVPN tunnel interface must be named vtunN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index bbaff5f04..bc7995918 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -7,7 +7,7 @@
<help>Point-to-Point Protocol over Ethernet (PPPoE)</help>
<priority>321</priority>
<constraint>
- <regex>pppoe[0-9]+$</regex>
+ <regex>^pppoe[0-9]+$</regex>
<validator name="numeric" argument="--range 1-99"/>
</constraint>
<constraintErrorMessage>PPPoE interface must be named pppoeN</constraintErrorMessage>
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index 772056bd2..0c56e4e4b 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -7,7 +7,7 @@
<help>Pseudo Ethernet</help>
<priority>319</priority>
<constraint>
- <regex>peth[0-9]+$</regex>
+ <regex>^peth[0-9]+$</regex>
</constraint>
<constraintErrorMessage>Pseudo Ethernet interface must be named pethN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index fe4a81f92..3ba82067f 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -7,7 +7,7 @@
<help>Tunnel interface</help>
<priority>380</priority>
<constraint>
- <regex>tun[0-9]+$</regex>
+ <regex>^tun[0-9]+$</regex>
</constraint>
<constraintErrorMessage>tunnel interface must be named tunN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 70c45d1fd..3108817b3 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -7,7 +7,7 @@
<help>Virtual Extensible LAN (VXLAN) Interface</help>
<priority>460</priority>
<constraint>
- <regex>vxlan[0-9]+$</regex>
+ <regex>^vxlan[0-9]+$</regex>
</constraint>
<constraintErrorMessage>VXLAN interface must be named vxlanN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index dd4a73efd..d461156b3 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -7,7 +7,7 @@
<help>WireGuard Interface</help>
<priority>459</priority>
<constraint>
- <regex>wg[0-9]+$</regex>
+ <regex>^wg[0-9]+$</regex>
</constraint>
<constraintErrorMessage>WireGuard interface must be named wgN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 2c224987e..12736510b 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -7,7 +7,7 @@
<help>Wireless (WiFi/WLAN) Network Interface</help>
<priority>400</priority>
<constraint>
- <regex>wlan[0-9]+$</regex>
+ <regex>^wlan[0-9]+$</regex>
</constraint>
<constraintErrorMessage>Wireless interface must be named wlanN</constraintErrorMessage>
<valueHelp>
diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in
index cea8f4029..2ec9205e0 100644
--- a/interface-definitions/interfaces-wirelessmodem.xml.in
+++ b/interface-definitions/interfaces-wirelessmodem.xml.in
@@ -7,7 +7,7 @@
<help>Wireless Modem (WWAN) Interface</help>
<priority>350</priority>
<constraint>
- <regex>wlm[0-9]+$</regex>
+ <regex>^wlm[0-9]+$</regex>
</constraint>
<constraintErrorMessage>Wireless Modem interface must be named wlmN</constraintErrorMessage>
<valueHelp>
@@ -43,6 +43,7 @@
</node>
#include <include/interface-description.xml.i>
#include <include/interface-disable.xml.i>
+ #include <include/interface-vrf.xml.i>
<leafNode name="device">
<properties>
<help>System device name (default: ttyUSB0)</help>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 76748e5ae..7c75bf824 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -17,9 +17,9 @@
<properties>
<help>VRF instance name</help>
<constraint>
- <regex>[^/\s]{1,16}$</regex>
+ <validator name="vrf-name"/>
</constraint>
- <constraintErrorMessage>VRF instance name must be 16 characters or less</constraintErrorMessage>
+ <constraintErrorMessage>VRF instance name must be 16 characters or less and can not\nbe named as regular network interfaces</constraintErrorMessage>
<valueHelp>
<format>name</format>
<description>Instance name</description>
@@ -44,4 +44,4 @@
</tagNode>
</children>
</node>
-</interfaceDefinition> \ No newline at end of file
+</interfaceDefinition>
diff --git a/python/vyos/ifconfig/__init__.py b/python/vyos/ifconfig/__init__.py
index d08a8b528..1f9956af0 100644
--- a/python/vyos/ifconfig/__init__.py
+++ b/python/vyos/ifconfig/__init__.py
@@ -36,3 +36,4 @@ from vyos.ifconfig.tunnel import IP6IP6If
from vyos.ifconfig.tunnel import SitIf
from vyos.ifconfig.tunnel import Sit6RDIf
from vyos.ifconfig.wireless import WiFiIf
+from vyos.ifconfig.l2tpv3 import L2TPv3If
diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py
index 07f1cf8a3..f0d64a53d 100644
--- a/python/vyos/ifconfig/l2tpv3.py
+++ b/python/vyos/ifconfig/l2tpv3.py
@@ -41,25 +41,27 @@ class L2TPv3If(Interface):
}
}
options = Interface.options + \
- ['tunnel_id', 'peer_tunnel_id', 'local_port', 'remote_port', 'encapsulation', 'local_address', 'remote_address']
+ ['tunnel_id', 'peer_tunnel_id', 'local_port', 'remote_port',
+ 'encapsulation', 'local_address', 'remote_address', 'session_id',
+ 'peer_session_id']
def _create(self):
# create tunnel interface
- cmd = 'ip l2tp add tunnel tunnel_id {} '.format(config['tunnel_id'])
- cmd += 'peer_tunnel_id {} '.format(config['peer_tunnel_id'])
- cmd += 'udp_sport {} '.format(config['local_port'])
- cmd += 'udp_dport {} '.format(config['remote_port'])
- cmd += 'encap {} '.format(config['encapsulation'])
- cmd += 'local {} '.format(config['local_address'])
- cmd += 'remote {} '.format(config['remote_address'])
- self._cmd(cmd)
+ cmd = 'ip l2tp add tunnel tunnel_id {tunnel_id}'
+ cmd += ' peer_tunnel_id {peer_tunnel_id}'
+ cmd += ' udp_sport {local_port}'
+ cmd += ' udp_dport {remote_port}'
+ cmd += ' encap {encapsulation}'
+ cmd += ' local {local_address}'
+ cmd += ' remote {remote_address}'
+ self._cmd(cmd.format(**self.config))
# setup session
- cmd = 'ip l2tp add session name {} '.format(self.config['ifname'])
- cmd += 'tunnel_id {} '.format(config['tunnel_id'])
- cmd += 'session_id {} '.format(config['session_id'])
- cmd += 'peer_session_id {} '.format(config['peer_session_id'])
- self._cmd(cmd)
+ cmd = 'ip l2tp add session name {ifname}'
+ cmd += ' tunnel_id {tunnel_id}'
+ cmd += ' session_id {session_id}'
+ cmd += ' peer_session_id {peer_session_id}'
+ self._cmd(cmd.format(**self.config))
# interface is always A/D down. It needs to be enabled explicitly
self.set_admin_state('down')
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 67aa87a3a..3970b8bf1 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -16,15 +16,6 @@
import os
import re
import sys
-import psutil
-
-import vyos.defaults
-
-from getpass import getuser
-from grp import getgrnam
-from time import sleep
-from subprocess import check_output
-from ipaddress import ip_network
def read_file(path):
""" Read a file to string """
@@ -33,6 +24,27 @@ def read_file(path):
return data
+def chown_file(path, user, group):
+ """ change file owner """
+ from pwd import getpwnam
+ from grp import getgrnam
+
+ if os.path.isfile(path):
+ uid = getpwnam(user).pw_uid
+ gid = getgrnam(group).gr_gid
+ os.chown(path, uid, gid)
+
+
+def chmod_x_file(path):
+ """ make file executable """
+ from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
+
+ if os.path.isfile(path):
+ bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | \
+ S_IROTH | S_IXOTH
+ os.chmod(path, bitmask)
+
+
def colon_separated_to_dict(data_string, uniquekeys=False):
""" Converts a string containing newline-separated entries
of colon-separated key-value pairs into a dict.
@@ -84,11 +96,12 @@ def colon_separated_to_dict(data_string, uniquekeys=False):
def process_running(pid_file):
""" Checks if a process with PID in pid_file is running """
+ from psutil import pid_exists
if not os.path.isfile(pid_file):
return False
with open(pid_file, 'r') as f:
pid = f.read().strip()
- return psutil.pid_exists(int(pid))
+ return pid_exists(int(pid))
def seconds_to_human(s, separator=""):
@@ -132,7 +145,10 @@ def seconds_to_human(s, separator=""):
def get_cfg_group_id():
- group_data = getgrnam(vyos.defaults.cfg_group)
+ from grp import getgrnam
+ from vyos.defaults import cfg_group
+
+ group_data = getgrnam(cfg_group)
return group_data.gr_gid
@@ -162,18 +178,22 @@ def commit_in_progress():
# Since this will be used in scripts that modify the config outside of the CLI
# framework, those knowingly have root permissions.
# For everything else, we add a safeguard.
+ from subprocess import check_output
+ from psutil import process_iter, NoSuchProcess
+ from vyos.defaults import commit_lock
+
id = check_output(['/usr/bin/id', '-u']).decode().strip()
if id != '0':
raise OSError("This functions needs root permissions to return correct results")
- for proc in psutil.process_iter():
+ for proc in process_iter():
try:
files = proc.open_files()
if files:
for f in files:
- if f.path == vyos.defaults.commit_lock:
+ if f.path == commit_lock:
return True
- except psutil.NoSuchProcess as err:
+ except NoSuchProcess as err:
# Process died before we could examine it
pass
# Default case
@@ -182,7 +202,7 @@ def commit_in_progress():
def wait_for_commit_lock():
""" Not to be used in normal op mode scripts! """
-
+ from time import sleep
# Very synchronous approach to multiprocessing
while commit_in_progress():
sleep(1)
@@ -206,17 +226,20 @@ def ask_yes_no(question, default=False) -> bool:
def is_admin() -> bool:
"""Look if current user is in sudo group"""
+ from getpass import getuser
+ from grp import getgrnam
current_user = getuser()
(_, _, _, admin_group_members) = getgrnam('sudo')
return current_user in admin_group_members
def mac2eui64(mac, prefix=None):
- '''
+ """
Convert a MAC address to a EUI64 address or, with prefix provided, a full
IPv6 address.
Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3
- '''
+ """
+ from ipaddress import ip_network
# http://tools.ietf.org/html/rfc4291#section-2.5.1
eui64 = re.sub(r'[.:-]', '', mac).lower()
eui64 = eui64[0:6] + 'fffe' + eui64[6:]
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index f318614db..9c045534c 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -20,12 +20,10 @@ 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 stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
from vyos.config import Config
from vyos.ifconfig import Interface
+from vyos.util import chown_file, chmod_x_file
from vyos import ConfigError
from netifaces import interfaces
@@ -112,15 +110,11 @@ if [ "$6" != "{{ intf }}" ]; then
exit
fi
+{% if ipv6_autoconf -%}
# add some info to syslog
DIALER_PID=$(cat /var/run/{{ intf }}.pid)
logger -t pppd[$DIALER_PID] "executing $0"
-logger -t pppd[$DIALER_PID] "configuring dialer interface $6 via $2"
-
-echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
-
-{% if ipv6_autoconf -%}
-
+logger -t pppd[$DIALER_PID] "configuring interface {{ intf }} via $2"
# Configure interface-specific Host/Router behaviour.
# Note: It is recommended to have the same setting on all interfaces; mixed
@@ -150,12 +144,12 @@ echo 1 > /proc/sys/net/ipv6/conf/{{ intf }}/autoconfigure
{% endif %}
"""
-config_pppoe_ip_pre_up_tmpl = """#!/bin/sh
+config_pppoe_ip_up_tmpl = """#!/bin/sh
# As PPPoE is an "on demand" interface we need to re-configure it when it
# becomes up
-if [ "$6" != "pppoe0" ]; then
+if [ "$6" != "{{ intf }}" ]; then
exit
fi
@@ -163,6 +157,8 @@ fi
DIALER_PID=$(cat /var/run/{{ intf }}.pid)
logger -t pppd[$DIALER_PID] "executing $0"
+echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
+
{% if vrf -%}
logger -t pppd[$DIALER_PID] "configuring dialer interface $6 for VRF {{ vrf }}"
ip link set dev {{ intf }} master {{ vrf }}
@@ -305,61 +301,49 @@ def verify(pppoe):
return None
def generate(pppoe):
- config_file_pppoe = f"/etc/ppp/peers/{pppoe['intf']}"
- ip_pre_up_script_file = f"/etc/ppp/ip-pre-up.d/9999-vyos-vrf-{pppoe['intf']}"
- ipv6_if_up_script_file = f"/etc/ppp/ipv6-up.d/50-vyos-{pppoe['intf']}-autoconf"
+ intf = pppoe['intf']
+ config_file_pppoe = f'/etc/ppp/peers/{intf}'
+ ip_up_script_file = f'/etc/ppp/ip-up.d/9990-vyos-vrf-{intf}'
+ ipv6_if_up_script_file = f'/etc/ppp/ipv6-up.d/9990-vyos-autoconf-{intf}'
+
+ config_files = [config_file_pppoe, ip_up_script_file, ipv6_if_up_script_file]
+
+ # Ensure directories for config files exist - otherwise create them on demand
+ for file in config_files:
+ dirname = os.path.dirname(file)
+ if not os.path.isdir(dirname):
+ os.mkdir(dirname)
# Always hang-up PPPoE connection prior generating new configuration file
- cmd = f"systemctl stop ppp@{pppoe['intf']}.service"
+ cmd = f'systemctl stop ppp@{intf}.service'
subprocess_cmd(cmd)
if pppoe['deleted']:
# Delete PPP configuration files
- if os.path.exists(config_file_pppoe):
- os.unlink(config_file_pppoe)
-
- if os.path.exists(ipv6_if_up_script_file):
- os.unlink(ipv6_if_up_script_file)
-
- if os.path.exists(ip_pre_up_script_file):
- os.unlink(ip_pre_up_script_file)
+ for file in config_files:
+ if os.path.exists(file):
+ os.unlink(file)
else:
- # PPP peers directory
- dirname = os.path.dirname(config_file_pppoe)
- if not os.path.isdir(dirname):
- os.mkdir(dirname)
-
# Create PPP configuration files
tmpl = Template(config_pppoe_tmpl)
config_text = tmpl.render(pppoe)
with open(config_file_pppoe, 'w') as f:
f.write(config_text)
- # PPP ip-pre-up.d scripting directory
- dirname = os.path.dirname(ip_pre_up_script_file)
- if not os.path.isdir(dirname):
- os.mkdir(dirname)
-
- tmpl = Template(config_pppoe_ip_pre_up_tmpl)
+ tmpl = Template(config_pppoe_ip_up_tmpl)
config_text = tmpl.render(pppoe)
- with open(ip_pre_up_script_file, 'w') as f:
+ with open(ip_up_script_file, 'w') as f:
f.write(config_text)
- # PPP ipv6-up.d scripting directory
- dirname = os.path.dirname(ipv6_if_up_script_file)
- if not os.path.isdir(dirname):
- os.mkdir(dirname)
-
tmpl = Template(config_pppoe_ipv6_up_tmpl)
config_text = tmpl.render(pppoe)
with open(ipv6_if_up_script_file, 'w') as f:
f.write(config_text)
- bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | \
- S_IROTH | S_IXOTH
- os.chmod(ip_pre_up_script_file, bitmask)
- os.chmod(ipv6_if_up_script_file, bitmask)
+ # make generated script file executable
+ chmod_x_file(ip_up_script_file)
+ chmod_x_file(ipv6_if_up_script_file)
return None
@@ -369,15 +353,13 @@ def apply(pppoe):
return None
if not pppoe['disable']:
- # dial PPPoE connection
- cmd = f"systemctl start ppp@{pppoe['intf']}.service"
+ # "dial" PPPoE connection
+ intf = pppoe['intf']
+ cmd = f'systemctl start ppp@{intf}.service'
subprocess_cmd(cmd)
# make logfile owned by root / vyattacfg
- if os.path.isfile(pppoe['logfile']):
- uid = getpwnam('root').pw_uid
- gid = getgrnam('vyattacfg').gr_gid
- os.chown(pppoe['logfile'], uid, gid)
+ chown_file(pppoe['logfile'], 'root', 'vyattacfg')
return None
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index 4cbb51f4a..646e61c53 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -138,6 +138,9 @@ default_config_data = {
'ip': False,
'ipv6': False,
'nhrp': [],
+ 'ipv6_autoconf': 0,
+ 'ipv6_forwarding': 1,
+ 'ipv6_dad_transmits': 1,
# internal
'tunnel': {},
# the following names are exactly matching the name
@@ -183,6 +186,9 @@ mapping = {
'link_detect': ('disable-link-detect', False, 2),
'vrf': ('vrf', False, None),
'addresses-add': ('address', True, None),
+ 'ipv6_autoconf': ('ipv6 address autoconf', False, 1),
+ 'ipv6_forwarding': ('ipv6 disable-forwarding', False, 0),
+ 'ipv6_dad_transmits:': ('ipv6 dup-addr-detect-transmits', False, None)
}
def get_class (options):
@@ -468,7 +474,8 @@ def apply(conf):
tunnel.set_interface(option, options[option])
# set other interface properties
- for option in ('alias', 'mtu', 'link_detect', 'multicast', 'allmulticast', 'vrf'):
+ for option in ('alias', 'mtu', 'link_detect', 'multicast', 'allmulticast',
+ 'vrf', 'ipv6_autoconf', 'ipv6_forwarding', 'ipv6_dad_transmits'):
tunnel.set_interface(option, options[option])
# Configure interface address(es)
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index 9efad3b8d..7aee5c9bd 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -20,10 +20,10 @@ 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 netifaces import interfaces
from vyos.config import Config
+from vyos.util import chown_file, chmod_x_file
from vyos import ConfigError
# Please be careful if you edit the template.
@@ -70,6 +70,32 @@ CONNECT ''
"""
+config_wwan_ip_up_tmpl = """#!/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" != "{{ intf }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
+
+{% if vrf -%}
+logger -t pppd[$DIALER_PID] "configuring interface {{ intf }} for VRF {{ vrf }}"
+ip link set dev {{ intf }} master {{ vrf }}
+{% endif %}
+
+"""
+
default_config_data = {
'address': [],
'apn': '',
@@ -84,7 +110,8 @@ default_config_data = {
'metric': '10',
'mtu': '1500',
'name_server': True,
- 'intf': ''
+ 'intf': '',
+ 'vrf': ''
}
def subprocess_cmd(command):
@@ -154,6 +181,10 @@ def get_config():
if conf.exists(['ondemand']):
wwan['on_demand'] = True
+ # retrieve VRF instance
+ if conf.exists('vrf'):
+ wwan['vrf'] = conf.return_value(['vrf'])
+
return wwan
def verify(wwan):
@@ -168,41 +199,58 @@ def verify(wwan):
if not os.path.exists(f"/dev/{wwan['device']}"):
raise ConfigError(f"Device {wwan['device']} does not exist")
+ vrf_name = wwan['vrf']
+ if vrf_name and vrf_name not in interfaces():
+ raise ConfigError(f'VRF {vrf_name} does not exist')
+
return None
def generate(wwan):
- config_file_wwan = f"/etc/ppp/peers/{wwan['intf']}"
+ intf = wwan['intf']
+ config_file_wwan = f'/etc/ppp/peers/{intf}'
+ config_file_wwan_chat = wwan['chat_script']
+ ip_up_script_file = f'/etc/ppp/ip-up.d/9991-vyos-vrf-{intf}'
+
+ config_files = [config_file_wwan, config_file_wwan_chat, ip_up_script_file]
+
+ # Ensure directories for config files exist - otherwise create them on demand
+ for file in config_files:
+ dirname = os.path.dirname(file)
+ if not os.path.isdir(dirname):
+ os.mkdir(dirname)
# Always hang-up WWAN connection prior generating new configuration file
- cmd = f"systemctl stop ppp@{wwan['intf']}.service"
+ cmd = f'systemctl stop ppp@{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'])
+ for file in config_files:
+ if os.path.exists(file):
+ os.unlink(file)
else:
- # PPP peers directory
- dirname = os.path.dirname(config_file_wwan)
- if not os.path.isdir(dirname):
- os.mkdir(dirname)
-
# 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)
+ # Create ip-pre-up script
+ tmpl = Template(config_wwan_ip_up_tmpl)
+ config_text = tmpl.render(wwan)
+ with open(ip_up_script_file, 'w') as f:
+ f.write(config_text)
+
+ # make generated script file executable
+ chmod_x_file(ip_up_script_file)
+
return None
def apply(wwan):
@@ -211,15 +259,13 @@ def apply(wwan):
return None
if not wwan['disable']:
- # dial WWAN connection
- cmd = f"systemctl start ppp@{wwan['intf']}.service"
+ # "dial" WWAN connection
+ intf = wwan['intf']
+ cmd = f'systemctl start ppp@{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)
+ chown_file(wwan['logfile'], 'root', 'vyattacfg')
return None
diff --git a/src/migration-scripts/quagga/5-to-6 b/src/migration-scripts/quagga/5-to-6
new file mode 100755
index 000000000..a71851942
--- /dev/null
+++ b/src/migration-scripts/quagga/5-to-6
@@ -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 parameter 'disable-network-import-check' which, as implemented,
+# had no effect on boot.
+
+import sys
+
+from vyos.configtree import ConfigTree
+
+
+if (len(sys.argv) < 2):
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+if not config.exists(['protocols', 'bgp']):
+ # Nothing to do
+ sys.exit(0)
+else:
+ # Check if BGP is actually configured and obtain the ASN
+ asn_list = config.list_nodes(['protocols', 'bgp'])
+ if asn_list:
+ # There's always just one BGP node, if any
+ asn = asn_list[0]
+ else:
+ # There's actually no BGP, just its empty shell
+ sys.exit(0)
+
+ # Check if BGP parameter disable-network-import-check exists
+ param = ['protocols', 'bgp', asn, 'parameters', 'disable-network-import-check']
+ if config.exists(param):
+ # Delete parameter
+ config.delete(param)
+ else:
+ # Do nothing
+ sys.exit(0)
+
+ 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)
diff --git a/src/validators/mac-address b/src/validators/mac-address
index d6ec6d712..435920b84 100755
--- a/src/validators/mac-address
+++ b/src/validators/mac-address
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,16 +13,14 @@
#
# 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 sys
import re
+from sys import exit, argv
-if len(sys.argv) == 2:
- pattern = "^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$"
- if re.match(pattern, sys.argv[1]):
- sys.exit(0)
- else:
- sys.exit(1)
+if len(argv) == 2:
+ pattern = "^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$"
+ if re.match(pattern, argv[1]):
+ exit(0)
+ else:
+ exit(1)
diff --git a/src/validators/vrf-name b/src/validators/vrf-name
new file mode 100755
index 000000000..11c453f4d
--- /dev/null
+++ b/src/validators/vrf-name
@@ -0,0 +1,40 @@
+#!/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 re
+from sys import exit, argv
+
+if len(argv) == 2:
+ len = len(argv[1])
+ # VRF instance name must be 16 characters or less, python range needs to be
+ # extended by one
+ if not len in range(1, 17):
+ exit(1)
+
+ # Treat loopback interface "lo" explicitly. Adding "lo" explicitly to the
+ # following regex pattern would deny any VRF name starting with lo - thuse
+ # local-vrf would be illegal - and that we do not want.
+ if argv[1] == "lo":
+ exit(1)
+
+ # VRF instances should not be named after regular interface names like bond0,
+ # br10 and so on - this can cause a lot of confusion/trouble
+ 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.+)?)?$).*$"
+ if re.match(pattern, argv[1]):
+ exit(0)
+
+exit(1)