summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/protocols-mpls.xml.in98
-rw-r--r--op-mode-definitions/show-mpls.xml62
-rw-r--r--python/vyos/ifconfig/control.py22
-rw-r--r--python/vyos/ifconfig/interface.py22
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py4
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py237
-rwxr-xr-xsrc/conf_mode/system-login-banner.py9
7 files changed, 430 insertions, 24 deletions
diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in
new file mode 100644
index 000000000..376323855
--- /dev/null
+++ b/interface-definitions/protocols-mpls.xml.in
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!-- Multiprotocol Label Switching (MPLS) configuration -->
+<interfaceDefinition>
+ <node name="protocols">
+ <children>
+ <node name="mpls" owner="${vyos_conf_scripts_dir}/protocols_mpls.py">
+ <properties>
+ <help>Multiprotocol Label Switching (MPLS)</help>
+ <priority>299</priority>
+ </properties>
+ <children>
+ <node name="ldp">
+ <properties>
+ <help>LDP options</help>
+ </properties>
+ <children>
+ <leafNode name="router-id">
+ <properties>
+ <help>x.x.x.x Label Switch Router (LSR) id</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>LSR ipv4 id</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="neighbor">
+ <properties>
+ <help>LDP Id of neighbor</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>neighbor IPv4 id</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="password">
+ <properties>
+ <help>Peer password</help>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="discovery">
+ <properties>
+ <help>Discovery parameters</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Discovery parameters</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="transport-ipv4-address">
+ <properties>
+ <help>Transport ipv4 address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 bind as transport</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="transport-ipv6-address">
+ <properties>
+ <help>Transport ipv6 address</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 bind as transport</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="interface">
+ <properties>
+ <help>Listen interface for LDP</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition> \ No newline at end of file
diff --git a/op-mode-definitions/show-mpls.xml b/op-mode-definitions/show-mpls.xml
new file mode 100644
index 000000000..6610788c7
--- /dev/null
+++ b/op-mode-definitions/show-mpls.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="mpls">
+ <children>
+ <node name="ldp">
+ <properties>
+ <help>Label Distribution Protocol (LDP)</help>
+ </properties>
+ <children>
+ <node name="binding">
+ <properties>
+ <help>Label Information Base</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls ldp binding"</command>
+ </node>
+ <node name="discovery">
+ <properties>
+ <help>Discovery hello information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls ldp discovery"</command>
+ </node>
+ <node name="interface">
+ <properties>
+ <help>LDP interface information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls ldp interface"</command>
+ </node>
+ <node name="neighbor">
+ <properties>
+ <help>LDP neighbor information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls ldp neighbor"</command>
+ <children>
+ <node name="detail">
+ <properties>
+ <help>Show neighbor detail</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls ldp neighbor detail"</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="pseudowire">
+ <properties>
+ <help>Show MPLS pseudowire interfaces</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls pseudowires"</command>
+ </node>
+ <node name="table">
+ <properties>
+ <help>Show MPLS table</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show mpls table"</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index 508b4e279..635b626e8 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -29,12 +29,13 @@ class Control:
def _cmd(self, command):
p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True)
tmp = p.communicate()[0].strip()
- self._debug_msg("cmd '{}'".format(command))
- if tmp.decode():
- self._debug_msg("returned:\n{}".format(tmp.decode()))
-
- # do we need some error checking code here?
- return tmp.decode()
+ self._debug_msg(f"cmd '{command}'")
+ decoded = tmp.decode()
+ if decoded:
+ self._debug_msg(f"returned:\n{decoded}")
+ if p.returncode != 0:
+ raise RuntimeError(f'{command}\nreturned: {decoded}')
+ return decoded
def _get_command(self, config, name):
"""
@@ -55,14 +56,17 @@ class Control:
validate = self._command_set[name].get('validate', None)
if validate:
- validate(value)
-
- config = {**config, **{'value': value}}
+ try:
+ validate(value)
+ except Exception as e:
+ raise e.__class__(f'Could not set {name}. {e}')
convert = self._command_set[name].get('convert', None)
if convert:
value = convert(value)
+ config = {**config, **{'value': value}}
+
cmd = self._command_set[name]['shellcmd'].format(**config)
return self._cmd(cmd)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index b002e0171..21ffa88f6 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -77,6 +77,10 @@ class Interface(Control):
}
_command_set = {
+ 'state': {
+ 'validate': lambda v: assert_list(v, ['up', 'down']),
+ 'shellcmd': 'ip link set dev {ifname} {value}',
+ },
'mac': {
'validate': assert_mac,
'shellcmd': 'ip link set dev {ifname} address {value}',
@@ -166,13 +170,15 @@ class Interface(Control):
if k in kargs:
self.config[k] = kargs[k]
- for k in self.required:
- if k not in kargs:
- raise ConfigError('missing required option {} for {}'.format(k,self.__class__))
-
if not os.path.exists('/sys/class/net/{}'.format(self.config['ifname'])):
if not self.config['type']:
raise Exception('interface "{}" not found'.format(self.config['ifname']))
+
+ for k in self.required:
+ if k not in kargs:
+ name = self.default['type']
+ raise ConfigError(f'missing required option {k} for {name} {ifname} creation')
+
self._create()
# per interface DHCP config files
@@ -452,13 +458,7 @@ class Interface(Control):
>>> Interface('eth0').get_state()
'down'
"""
- if state not in ['up', 'down']:
- raise ValueError('state must be "up" or "down"')
-
- # Assemble command executed on system. Unfortunately there is no way
- # to up/down an interface via sysfs
- cmd = 'ip link set dev {} {}'.format(self.config['ifname'], state)
- return self._cmd(cmd)
+ return self.set_interface('state', state)
def set_proxy_arp(self, enable):
"""
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 6ccbaed5b..463ae9fd7 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -365,7 +365,7 @@ def fixup_permission(filename, permission=S_IRUSR):
def checkCertHeader(header, filename):
"""
Verify if filename contains specified header.
- Returns True on success or on file not found to not trigger the exceptions
+ Returns True if match is found, False if no match or file is not found
"""
if not os.path.isfile(filename):
return False
@@ -375,7 +375,7 @@ def checkCertHeader(header, filename):
if re.match(header, line):
return True
- return True
+ return False
def get_config():
openvpn = deepcopy(default_config_data)
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
new file mode 100755
index 000000000..5f2497660
--- /dev/null
+++ b/src/conf_mode/protocols_mpls.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 sys
+import jinja2
+import copy
+import os
+import vyos.validate
+
+from vyos import ConfigError
+from vyos.config import Config
+
+config_file = r'/tmp/ldpd.frr'
+
+# Please be careful if you edit the template.
+config_tmpl = """
+!
+{% if mpls_ldp -%}
+mpls ldp
+{% if old_router_id -%}
+no router-id {{ old_router_id }}
+{% endif -%}
+{% if router_id -%}
+router-id {{ router_id }}
+{% endif -%}
+{% for neighbor_id in old_ldp.neighbors -%}
+no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}}
+{% endfor -%}
+{% for neighbor_id in ldp.neighbors -%}
+neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
+{% endfor -%}
+address-family ipv4
+label local allocate host-routes
+{% if old_ldp.d_transp_ipv4 -%}
+no discovery transport-address {{ old_ldp.d_transp_ipv4 }}
+{% endif -%}
+{% if ldp.d_transp_ipv4 -%}
+discovery transport-address {{ ldp.d_transp_ipv4 }}
+{% endif -%}
+{% for interface in old_ldp.interfaces -%}
+no interface {{interface}}
+{% endfor -%}
+{% for interface in ldp.interfaces -%}
+interface {{interface}}
+{% endfor -%}
+!
+!
+exit-address-family
+!
+{% if ldp.d_transp_ipv6 -%}
+address-family ipv6
+label local allocate host-routes
+{% if old_ldp.d_transp_ipv6 -%}
+no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
+{% endif -%}
+{% if ldp.d_transp_ipv6 -%}
+discovery transport-address {{ ldp.d_transp_ipv6 }}
+{% endif -%}
+{% for interface in old_ldp.interfaces -%}
+no interface {{interface}}
+{% endfor -%}
+{% for interface in ldp.interfaces -%}
+interface {{interface}}
+{% endfor -%}
+!
+exit-address-family
+{% else -%}
+no address-family ipv6
+{% endif -%}
+!
+{% else -%}
+no mpls ldp
+{% endif -%}
+!
+"""
+
+def sysctl(name, value):
+ os.system('sysctl -wq {}={}'.format(name, value))
+
+def get_config():
+ conf = Config()
+ mpls_conf = {
+ 'router_id' : None,
+ 'mpls_ldp' : False,
+ 'old_ldp' : {
+ 'interfaces' : [],
+ 'neighbors' : {},
+ 'd_transp_ipv4' : None,
+ 'd_transp_ipv6' : None
+ },
+ 'ldp' : {
+ 'interfaces' : [],
+ 'neighbors' : {},
+ 'd_transp_ipv4' : None,
+ 'd_transp_ipv6' : None
+ }
+ }
+ if not (conf.exists('protocols mpls') or conf.exists_effective('protocols mpls')):
+ return None
+
+ if conf.exists('protocols mpls ldp'):
+ mpls_conf['mpls_ldp'] = True
+
+ conf.set_level('protocols mpls ldp')
+
+ # Get router-id
+ if conf.exists_effective('router-id'):
+ mpls_conf['old_router_id'] = conf.return_effective_value('router-id')
+ if conf.exists('router-id'):
+ mpls_conf['router_id'] = conf.return_value('router-id')
+
+ # Get discovery transport-ipv4-address
+ if conf.exists_effective('discovery transport-ipv4-address'):
+ mpls_conf['old_ldp']['d_transp_ipv4'] = conf.return_effective_value('discovery transport-ipv4-address')
+
+ if conf.exists('discovery transport-ipv4-address'):
+ mpls_conf['ldp']['d_transp_ipv4'] = conf.return_value('discovery transport-ipv4-address')
+
+ # Get discovery transport-ipv6-address
+ if conf.exists_effective('discovery transport-ipv6-address'):
+ mpls_conf['old_ldp']['d_transp_ipv6'] = conf.return_effective_value('discovery transport-ipv6-address')
+
+ if conf.exists('discovery transport-ipv6-address'):
+ mpls_conf['ldp']['d_transp_ipv6'] = conf.return_value('discovery transport-ipv6-address')
+
+ # Get interfaces
+ if conf.exists_effective('interface'):
+ mpls_conf['old_ldp']['interfaces'] = conf.return_effective_values('interface')
+
+ if conf.exists('interface'):
+ mpls_conf['ldp']['interfaces'] = conf.return_values('interface')
+
+ # Get neighbors
+ for neighbor in conf.list_effective_nodes('neighbor'):
+ mpls_conf['old_ldp']['neighbors'].update({
+ neighbor : {
+ 'password' : conf.return_effective_value('neighbor {0} password'.format(neighbor))
+ }
+ })
+
+ for neighbor in conf.list_nodes('neighbor'):
+ mpls_conf['ldp']['neighbors'].update({
+ neighbor : {
+ 'password' : conf.return_value('neighbor {0} password'.format(neighbor))
+ }
+ })
+
+ # print(mpls_conf)
+ # sys.exit(1)
+ return mpls_conf
+
+def operate_mpls_on_intfc(interfaces, action):
+ rp_filter = 0
+ if action == 1:
+ rp_filter = 2
+ for iface in interfaces:
+ sysctl('net.mpls.conf.{0}.input'.format(iface), action)
+ # Operate rp filter
+ sysctl('net.ipv4.conf.{0}.rp_filter'.format(iface), rp_filter)
+
+def verify(mpls):
+ if mpls is None:
+ return None
+
+ if mpls['mpls_ldp']:
+ # Requre router-id
+ if not mpls['router_id']:
+ raise ConfigError(f"MPLS ldp router-id is mandatory!")
+
+ # Requre discovery transport-address
+ if not mpls['ldp']['d_transp_ipv4'] and not mpls['ldp']['d_transp_ipv6']:
+ raise ConfigError(f"MPLS ldp discovery transport address is mandatory!")
+
+ # Requre interface
+ if not mpls['ldp']['interfaces']:
+ raise ConfigError(f"MPLS ldp interface is mandatory!")
+
+def generate(mpls):
+ if mpls is None:
+ return None
+
+ tmpl = jinja2.Template(config_tmpl)
+ config_text = tmpl.render(mpls)
+ with open(config_file, 'w') as f:
+ f.write(config_text)
+
+ return None
+
+def apply(mpls):
+ if mpls is None:
+ return None
+
+ # Set number of entries in the platform label table
+ if mpls['mpls_ldp']:
+ sysctl('net.mpls.platform_labels', '1048575')
+ else:
+ sysctl('net.mpls.platform_labels', '0')
+
+ # Do not copy IP TTL to MPLS header
+ sysctl('net.mpls.ip_ttl_propagate', '0')
+
+ # Allow mpls on interfaces
+ operate_mpls_on_intfc(mpls['ldp']['interfaces'], 1)
+
+ # Disable mpls on deleted interfaces
+ diactive_ifaces = set(mpls['old_ldp']['interfaces']).difference(mpls['ldp']['interfaces'])
+ operate_mpls_on_intfc(diactive_ifaces, 0)
+
+ if os.path.exists(config_file):
+ os.system("sudo vtysh -d ldpd -f " + config_file)
+ os.remove(config_file)
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/conf_mode/system-login-banner.py b/src/conf_mode/system-login-banner.py
index 20cc16f97..5a34a0b06 100755
--- a/src/conf_mode/system-login-banner.py
+++ b/src/conf_mode/system-login-banner.py
@@ -25,6 +25,7 @@ individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
+
"""
PRELOGIN_FILE = r'/etc/issue'
@@ -32,8 +33,8 @@ PRELOGIN_NET_FILE = r'/etc/issue.net'
POSTLOGIN_FILE = r'/etc/motd'
default_config_data = {
- 'issue': 'Welcome to VyOS - \n \l',
- 'issue_net': 'Welcome to VyOS',
+ 'issue': 'Welcome to VyOS - \n \l\n',
+ 'issue_net': 'Welcome to VyOS\n',
'motd': motd
}
@@ -54,6 +55,8 @@ def get_config():
if tmp:
tmp = tmp.replace('\\n','\n')
tmp = tmp.replace('\\t','\t')
+ # always add newline character
+ tmp += '\n'
else:
tmp = ''
@@ -66,6 +69,8 @@ def get_config():
if tmp:
tmp = tmp.replace('\\n','\n')
tmp = tmp.replace('\\t','\t')
+ # always add newline character
+ tmp += '\n'
else:
tmp = ''