summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriyEshenko <dmitriy.eshenko@vyos.io>2020-03-18 22:29:54 +0000
committerDmitriyEshenko <dmitriy.eshenko@vyos.io>2020-03-18 22:29:54 +0000
commit160cdee4879e7fb472ce0fa46e3ce9e21a04cc17 (patch)
tree4f80c91510a9f5e92d92683355d675c176d51d3d
parent3b8e691566bd12e931f57ac8e6a0a22dadc39da3 (diff)
downloadvyos-1x-160cdee4879e7fb472ce0fa46e3ce9e21a04cc17.tar.gz
vyos-1x-160cdee4879e7fb472ce0fa46e3ce9e21a04cc17.zip
mpls: T915: Basic MPLS implementation
-rw-r--r--interface-definitions/protocols-mpls.xml.in98
-rw-r--r--op-mode-definitions/show-mpls.xml62
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py237
3 files changed, 397 insertions, 0 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/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)