-
-
Save thomas-mangin/7704c538d905190bd05cfe613bd9f4f5 to your computer and use it in GitHub Desktop.
T31: patch-1 review
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/interface-definitions/include/interface-vrf.xml.i b/interface-definitions/include/interface-vrf.xml.i | |
new file mode 100644 | |
index 0000000..7e880e6 | |
--- /dev/null | |
+++ b/interface-definitions/include/interface-vrf.xml.i | |
@@ -0,0 +1,12 @@ | |
+<leafNode name="vrf"> | |
+ <properties> | |
+ <help>VRF instance name</help> | |
+ <completionHelp> | |
+ <path>vrf name</path> | |
+ </completionHelp> | |
+ <constraint> | |
+ <validator name="interface-name"/> | |
+ </constraint> | |
+ <constraintErrorMessage>VRF name not allowed or to long</constraintErrorMessage> | |
+ </properties> | |
+</leafNode> | |
diff --git a/interface-definitions/include/vif-s.xml.i b/interface-definitions/include/vif-s.xml.i | |
index 2120aa3..c147066 100644 | |
--- a/interface-definitions/include/vif-s.xml.i | |
+++ b/interface-definitions/include/vif-s.xml.i | |
@@ -8,6 +8,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6-dhcp.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/dhcp-dhcpv6-options.xml.i> | |
#include <include/interface-disable-link-detect.xml.i> | |
diff --git a/interface-definitions/include/vif.xml.i b/interface-definitions/include/vif.xml.i | |
index 85e9018..1f65b42 100644 | |
--- a/interface-definitions/include/vif.xml.i | |
+++ b/interface-definitions/include/vif.xml.i | |
@@ -12,6 +12,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6-dhcp.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/dhcp-dhcpv6-options.xml.i> | |
#include <include/interface-disable-link-detect.xml.i> | |
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in | |
index 586a843..786be1e 100644 | |
--- a/interface-definitions/interfaces-bonding.xml.in | |
+++ b/interface-definitions/interfaces-bonding.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6-dhcp.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
<node name="arp-monitor"> | |
<properties> | |
<help>ARP link monitoring parameters</help> | |
@@ -156,6 +157,7 @@ | |
</leafNode> | |
#include <include/vif-s.xml.i> | |
#include <include/vif.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
</children> | |
</tagNode> | |
</children> | |
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in | |
index e8285b1..4c43b42 100644 | |
--- a/interface-definitions/interfaces-bridge.xml.in | |
+++ b/interface-definitions/interfaces-bridge.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6-dhcp.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
<leafNode name="aging"> | |
<properties> | |
<help>MAC address aging interval</help> | |
diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in | |
index 39809a6..326939e 100644 | |
--- a/interface-definitions/interfaces-dummy.xml.in | |
+++ b/interface-definitions/interfaces-dummy.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/interface-disable.xml.i> | |
</children> | |
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in | |
index 8f5d735..f236790 100644 | |
--- a/interface-definitions/interfaces-ethernet.xml.in | |
+++ b/interface-definitions/interfaces-ethernet.xml.in | |
@@ -21,6 +21,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6-dhcp.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/dhcp-dhcpv6-options.xml.i> | |
<leafNode name="disable-flow-control"> | |
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in | |
index a6406ff..302eada 100644 | |
--- a/interface-definitions/interfaces-geneve.xml.in | |
+++ b/interface-definitions/interfaces-geneve.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/interface-disable.xml.i> | |
<node name="ip"> | |
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in | |
index a408e58..bc5b7c0 100644 | |
--- a/interface-definitions/interfaces-l2tpv3.xml.in | |
+++ b/interface-definitions/interfaces-l2tpv3.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
<leafNode name="destination-port"> | |
<properties> | |
diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in | |
index ddbfad7..2152bb7 100644 | |
--- a/interface-definitions/interfaces-loopback.xml.in | |
+++ b/interface-definitions/interfaces-loopback.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
</children> | |
</tagNode> | |
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in | |
index bc1a159..5de7bb7 100644 | |
--- a/interface-definitions/interfaces-openvpn.xml.in | |
+++ b/interface-definitions/interfaces-openvpn.xml.in | |
@@ -34,6 +34,7 @@ | |
</children> | |
</node> | |
#include <include/interface-description.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
<leafNode name="device-type"> | |
<properties> | |
<help>OpenVPN interface device-type</help> | |
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in | |
index 16cb2c1..147cbab 100644 | |
--- a/interface-definitions/interfaces-vxlan.xml.in | |
+++ b/interface-definitions/interfaces-vxlan.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/interface-disable.xml.i> | |
<leafNode name="group"> | |
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in | |
index dd4a73e..9f8ac9a 100644 | |
--- a/interface-definitions/interfaces-wireguard.xml.in | |
+++ b/interface-definitions/interfaces-wireguard.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
#include <include/interface-description.xml.i> | |
#include <include/interface-disable.xml.i> | |
<leafNode name="port"> | |
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in | |
index d6b2579..6cc6f70 100644 | |
--- a/interface-definitions/interfaces-wireless.xml.in | |
+++ b/interface-definitions/interfaces-wireless.xml.in | |
@@ -17,6 +17,7 @@ | |
</properties> | |
<children> | |
#include <include/address-ipv4-ipv6-dhcp.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
<node name="capabilities"> | |
<properties> | |
<help>HT and VHT capabilities for your card</help> | |
@@ -754,6 +755,7 @@ | |
</properties> | |
</leafNode> | |
#include <include/vif.xml.i> | |
+ #include <include/interface-vrf.xml.i> | |
</children> | |
</tagNode> | |
</children> | |
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in | |
index ea42021..2258c18 100644 | |
--- a/interface-definitions/ssh.xml.in | |
+++ b/interface-definitions/ssh.xml.in | |
@@ -9,6 +9,7 @@ | |
<priority>500</priority> | |
</properties> | |
<children> | |
+ #include <include/interface-vrf.xml.i> | |
<node name="access-control"> | |
<properties> | |
<help>SSH user/group access controls. Directives are processed | |
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in | |
new file mode 100644 | |
index 0000000..e270e8b | |
--- /dev/null | |
+++ b/interface-definitions/vrf.xml.in | |
@@ -0,0 +1,58 @@ | |
+<?xml version="1.0"?> | |
+<interfaceDefinition> | |
+ <node name="vrf" owner="${vyos_conf_scripts_dir}/vrf.py"> | |
+ <properties> | |
+ <help>VRF configuration</help> | |
+ <!-- must be before any interface creation --> | |
+ <priority>210</priority> | |
+ </properties> | |
+ <children> | |
+ <node name="disable-bind-to-all"> | |
+ <properties> | |
+ <help>Disable services running on the default VRF from other VRF (ssh, bgp, ...)</help> | |
+ </properties> | |
+ <children> | |
+ <leafNode name="ipv4"> | |
+ <properties> | |
+ <valueless/> | |
+ <help>Enable binding across all VRF domains for IPv4</help> | |
+ </properties> | |
+ </leafNode> | |
+ </children> | |
+ </node> | |
+ <tagNode name="name"> | |
+ <properties> | |
+ <help>Virtual Routing and Forwarding</help> | |
+ <constraint> | |
+ <validator name="interface-name"/> | |
+ </constraint> | |
+ <constraintErrorMessage>VRF name not allowed or to long</constraintErrorMessage> | |
+ <valueHelp> | |
+ <format>name</format> | |
+ <description>the vrf name must not contain '/' and be 16 characters or less</description> | |
+ </valueHelp> | |
+ </properties> | |
+ <children> | |
+ <leafNode name="table"> | |
+ <properties> | |
+ <help>The routing table to associate to this VRF</help> | |
+ <constraint> | |
+ <validator name="numeric" argument="--range 1-2147483647"/> | |
+ </constraint> | |
+ <constraintErrorMessage>Invalid kernel table number</constraintErrorMessage> | |
+ <valueHelp> | |
+ <format>number</format> | |
+ <description>the VRF must be a number between 1 and 2^31-1</description> | |
+ </valueHelp> | |
+ </properties> | |
+ </leafNode> | |
+ <leafNode name="description"> | |
+ <properties> | |
+ <help>Description of the VRF role</help> | |
+ </properties> | |
+ </leafNode> | |
+ </children> | |
+ </tagNode> | |
+ </children> | |
+ </node> | |
+</interfaceDefinition> | |
\ No newline at end of file | |
diff --git a/op-mode-definitions/show-vrf.xml b/op-mode-definitions/show-vrf.xml | |
new file mode 100644 | |
index 0000000..fb2fddd | |
--- /dev/null | |
+++ b/op-mode-definitions/show-vrf.xml | |
@@ -0,0 +1,24 @@ | |
+<?xml version="1.0"?> | |
+<interfaceDefinition> | |
+ <node name="show"> | |
+ <children> | |
+ <node name="vrf"> | |
+ <properties> | |
+ <help>Show VRF information</help> | |
+ </properties> | |
+ <command>${vyos_completion_dir}/list_vrf.py -e</command> | |
+ <children> | |
+ <tagNode name="name"> | |
+ <properties> | |
+ <help>Show VRF information for an interface</help> | |
+ <completionHelp> | |
+ <script>${vyos_completion_dir}/list_vrf.py</script> | |
+ </completionHelp> | |
+ </properties> | |
+ <command>${vyos_completion_dir}/list_vrf.py -e "$4"</command> | |
+ </tagNode> | |
+ </children> | |
+ </node> | |
+ </children> | |
+ </node> | |
+</interfaceDefinition> | |
diff --git a/python/vyos/config.py b/python/vyos/config.py | |
index 2342f70..88dd67e 100644 | |
--- a/python/vyos/config.py | |
+++ b/python/vyos/config.py | |
@@ -262,7 +262,7 @@ class Config(object): | |
Args: path (str list): Configuration tree path, can be empty | |
Returns: a dict representation of the config | |
""" | |
- res = self.show_config(self._make_path(path), effective=effective) | |
+ res = self.show_config(self._make_path(path), default='', effective=effective) | |
config_tree = vyos.configtree.ConfigTree(res) | |
config_dict = json.loads(config_tree.to_json()) | |
return config_dict | |
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py | |
index 90b8fc1..3d6f48a 100644 | |
--- a/python/vyos/ifconfig.py | |
+++ b/python/vyos/ifconfig.py | |
@@ -66,7 +66,7 @@ interface "{{ intf }}" { | |
""" | |
class Interface: | |
- def __init__(self, ifname, type=None): | |
+ def __init__(self, ifname, type=None, vrf=''): | |
""" | |
This is the base interface class which supports basic IP/MAC address | |
operations as well as DHCP(v6). Other interface which represent e.g. | |
@@ -91,6 +91,15 @@ class Interface: | |
cmd = 'ip link add dev {} type {}'.format(self._ifname, type) | |
self._cmd(cmd) | |
+ if vrf: | |
+ cmd = 'ip link set {} master {}'.format(self._ifname,vrf) | |
+ res = self._cmd(cmd) | |
+ if res: print(res) | |
+ else: | |
+ cmd = 'ip link set dev {} nomaster'.format(self._ifname) | |
+ res = self._cmd(cmd) | |
+ if res: print(res) | |
+ | |
# per interface DHCP config files | |
self._dhcp_cfg_file = dhclient_base + self._ifname + '.conf' | |
self._dhcp_pid_file = dhclient_base + self._ifname + '.pid' | |
@@ -812,8 +821,8 @@ class LoopbackIf(Interface): | |
uses to communicate with itself. | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname, type='loopback') | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, type='loopback', vrf=vrf) | |
def remove(self): | |
""" | |
@@ -839,8 +848,8 @@ class DummyIf(Interface): | |
packets through without actually transmitting them. | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname, type='dummy') | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, type='dummy', vrf=vrf) | |
class STPIf(Interface): | |
@@ -848,8 +857,8 @@ class STPIf(Interface): | |
A spanning-tree capable interface. This applies only to bridge port member | |
interfaces! | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname) | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, vrf=vrf) | |
def set_path_cost(self, cost): | |
""" | |
@@ -895,8 +904,8 @@ class BridgeIf(Interface): | |
The Linux bridge code implements a subset of the ANSI/IEEE 802.1d standard. | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname, type='bridge') | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, type='bridge', vrf=vrf) | |
def set_ageing_time(self, time): | |
""" | |
@@ -1022,8 +1031,8 @@ class VLANIf(Interface): | |
This class handels the creation and removal of a VLAN interface. It serves | |
as base class for BondIf and EthernetIf. | |
""" | |
- def __init__(self, ifname, type=None): | |
- super().__init__(ifname, type) | |
+ def __init__(self, ifname, type=None, vrf =''): | |
+ super().__init__(ifname, type, vrf=vrf) | |
def remove(self): | |
""" | |
@@ -1060,7 +1069,7 @@ class VLANIf(Interface): | |
super().remove() | |
- def add_vlan(self, vlan_id, ethertype='', ingress_qos='', egress_qos=''): | |
+ def add_vlan(self, vlan_id, ethertype='', ingress_qos='', egress_qos='', vrf=''): | |
""" | |
A virtual LAN (VLAN) is any broadcast domain that is partitioned and | |
isolated in a computer network at the data link layer (OSI layer 2). | |
@@ -1110,7 +1119,7 @@ class VLANIf(Interface): | |
# return new object mapping to the newly created interface | |
# we can now work on this object for e.g. IP address setting | |
# or interface description and so on | |
- return VLANIf(vlan_ifname) | |
+ return VLANIf(vlan_ifname, vrf=vrf) | |
def del_vlan(self, vlan_id): | |
@@ -1132,8 +1141,8 @@ class EthernetIf(VLANIf): | |
""" | |
Abstraction of a Linux Ethernet Interface | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname) | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, vrf=vrf) | |
def get_driver_name(self): | |
""" | |
@@ -1334,8 +1343,8 @@ class BondIf(VLANIf): | |
either hot standby or load balancing services. Additionally, link integrity | |
monitoring may be performed. | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname, type='bond') | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, type='bond', vrf=vrf) | |
def remove(self): | |
""" | |
@@ -1567,8 +1576,8 @@ class WireGuardIf(Interface): | |
'allowed-ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'} | |
""" | |
- def __init__(self, ifname): | |
- super().__init__(ifname, type='wireguard') | |
+ def __init__(self, ifname, vrf=''): | |
+ super().__init__(ifname, type='wireguard', vrf=vrf) | |
self.config = { | |
'port': 0, | |
@@ -1708,7 +1717,7 @@ class VXLANIf(Interface): | |
For more information please refer to: | |
https://www.kernel.org/doc/Documentation/networking/vxlan.txt | |
""" | |
- def __init__(self, ifname, config=''): | |
+ def __init__(self, ifname, config='',vrf=''): | |
if config: | |
self._ifname = ifname | |
@@ -1729,7 +1738,7 @@ class VXLANIf(Interface): | |
.format(intf=self._ifname, vni=config['vni'], grp_rem=group, dev=dev, port=config['port']) | |
self._cmd(cmd) | |
- super().__init__(ifname, type='vxlan') | |
+ super().__init__(ifname, type='vxlan', vrf=vrf) | |
@staticmethod | |
def get_config(): | |
@@ -1761,7 +1770,7 @@ class GeneveIf(Interface): | |
https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/#geneve | |
https://lwn.net/Articles/644938/ | |
""" | |
- def __init__(self, ifname, config=''): | |
+ def __init__(self, ifname, config='', vrf=''): | |
if config: | |
self._ifname = ifname | |
if not os.path.exists('/sys/class/net/{}'.format(self._ifname)): | |
@@ -1772,7 +1781,7 @@ class GeneveIf(Interface): | |
# interface is always A/D down. It needs to be enabled explicitly | |
self.set_state('down') | |
- super().__init__(ifname, type='geneve') | |
+ super().__init__(ifname, type='geneve', vrf=vrf) | |
@staticmethod | |
def get_config(): | |
@@ -1798,7 +1807,7 @@ class L2TPv3If(Interface): | |
either hot standby or load balancing services. Additionally, link integrity | |
monitoring may be performed. | |
""" | |
- def __init__(self, ifname, config=''): | |
+ def __init__(self, ifname, config='',vrf=''): | |
self._config = {} | |
if config: | |
self._ifname = ifname | |
@@ -1824,7 +1833,7 @@ class L2TPv3If(Interface): | |
# interface is always A/D down. It needs to be enabled explicitly | |
self.set_state('down') | |
- super().__init__(ifname, type='l2tp') | |
+ super().__init__(ifname, type='l2tp',vrf=vrf) | |
def remove(self): | |
""" | |
diff --git a/python/vyos/vrf.py b/python/vyos/vrf.py | |
new file mode 100644 | |
index 0000000..99e4cb7 | |
--- /dev/null | |
+++ b/python/vyos/vrf.py | |
@@ -0,0 +1,23 @@ | |
+# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> | |
+# | |
+# 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 <http://www.gnu.org/licenses/>. | |
+ | |
+import json | |
+import subprocess | |
+ | |
+ | |
+def list_vrfs(): | |
+ command = 'ip -j -br link show type vrf' | |
+ answer = json.loads(subprocess.check_output(command.split()).decode()) | |
+ return [_ for _ in answer if _] | |
diff --git a/src/completion/list_vrf.py b/src/completion/list_vrf.py | |
new file mode 100755 | |
index 0000000..210b3c9 | |
--- /dev/null | |
+++ b/src/completion/list_vrf.py | |
@@ -0,0 +1,27 @@ | |
+#!/usr/bin/env python3 | |
+ | |
+import argparse | |
+import vyos.vrf | |
+ | |
+parser = argparse.ArgumentParser() | |
+group = parser.add_mutually_exclusive_group() | |
+group.add_argument("-e", "--extensive", action="store_true", | |
+ help="provide detailed vrf informatio") | |
+parser.add_argument('interface', metavar='I', type=str, nargs='?', | |
+ help='interface to display') | |
+ | |
+args = parser.parse_args() | |
+ | |
+if args.extensive: | |
+ print('{:16} {:7} {:17} {}'.format('interface', 'state', 'mac', 'flags')) | |
+ print('{:16} {:7} {:17} {}'.format('---------', '-----', '---', '-----')) | |
+ for vrf in vyos.vrf.list_vrfs(): | |
+ name = vrf['ifname'] | |
+ if args.interface and name != args.interface: | |
+ continue | |
+ state = vrf['operstate'].lower() | |
+ mac = vrf['address'].lower() | |
+ info = ','.join([_.lower() for _ in vrf['flags']]) | |
+ print(f'{name:16} {state:7} {mac:17} {info}') | |
+else: | |
+ print(" ".join([vrf['ifname'] for vrf in vyos.vrf.list_vrfs()])) | |
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py | |
index ac4f213..2244589 100755 | |
--- a/src/conf_mode/interfaces-bonding.py | |
+++ b/src/conf_mode/interfaces-bonding.py | |
@@ -58,7 +58,8 @@ default_config_data = { | |
'vif_s': [], | |
'vif_s_remove': [], | |
'vif': [], | |
- 'vif_remove': [] | |
+ 'vif_remove': [], | |
+ 'vrf': '', | |
} | |
@@ -244,6 +245,12 @@ def get_config(): | |
conf.set_level(cfg_base + ' vif ' + vif) | |
bond['vif'].append(vlan_to_dict(conf)) | |
+ # re-set configuration level to parse new nodes | |
+ conf.set_level(cfg_base) | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ bond['vrf'] = conf.return_value('vrf') | |
+ | |
return bond | |
@@ -343,12 +350,12 @@ def generate(bond): | |
return None | |
def apply(bond): | |
- b = BondIf(bond['intf']) | |
- | |
if bond['deleted']: | |
+ b = BondIf(bond['intf']) | |
# delete interface | |
b.remove() | |
else: | |
+ b = BondIf(bond['intf'], vrf=bond['vrf']) | |
# ARP link monitoring frequency, reset miimon when arp-montior is inactive | |
# this is done inside BondIf automatically | |
b.set_arp_interval(bond['arp_mon_intvl']) | |
@@ -469,7 +476,7 @@ def apply(bond): | |
# create service VLAN interfaces (vif-s) | |
for vif_s in bond['vif_s']: | |
- s_vlan = b.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) | |
+ s_vlan = b.add_vlan(vif_s['id'], ethertype=vif_s['ethertype'], vrf=bond['vrf']) | |
apply_vlan_config(s_vlan, vif_s) | |
# remove no longer required client VLAN interfaces (vif-c) | |
@@ -480,7 +487,7 @@ def apply(bond): | |
# create client VLAN interfaces (vif-c) | |
# on lower service VLAN interface | |
for vif_c in vif_s['vif_c']: | |
- c_vlan = s_vlan.add_vlan(vif_c['id']) | |
+ c_vlan = s_vlan.add_vlan(vif_c['id'], vrf=bond['vrf']) | |
apply_vlan_config(c_vlan, vif_c) | |
# remove no longer required VLAN interfaces (vif) | |
@@ -489,7 +496,7 @@ def apply(bond): | |
# create VLAN interfaces (vif) | |
for vif in bond['vif']: | |
- vlan = b.add_vlan(vif['id']) | |
+ vlan = b.add_vlan(vif['id'], vrf=bond['vrf']) | |
apply_vlan_config(vlan, vif) | |
return None | |
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py | |
index a3213f3..ffbb5a4 100755 | |
--- a/src/conf_mode/interfaces-bridge.py | |
+++ b/src/conf_mode/interfaces-bridge.py | |
@@ -52,7 +52,8 @@ default_config_data = { | |
'member': [], | |
'member_remove': [], | |
'priority': 32768, | |
- 'stp': 0 | |
+ 'stp': 0, | |
+ 'vrf': '', | |
} | |
def get_config(): | |
@@ -191,6 +192,10 @@ def get_config(): | |
if conf.exists('stp'): | |
bridge['stp'] = 1 | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ bridge['vrf'] = conf.return_value('vrf') | |
+ | |
return bridge | |
def verify(bridge): | |
@@ -226,12 +231,12 @@ def generate(bridge): | |
return None | |
def apply(bridge): | |
- br = BridgeIf(bridge['intf']) | |
- | |
if bridge['deleted']: | |
+ br = BridgeIf(bridge['intf']) | |
# delete interface | |
br.remove() | |
else: | |
+ br = BridgeIf(bridge['intf'], vrf=bridge['vrf']) | |
# enable interface | |
br.set_state('up') | |
# set ageing time | |
@@ -312,7 +317,7 @@ def apply(bridge): | |
# configure additional bridge member options | |
for member in bridge['member']: | |
- i = STPIf(member['name']) | |
+ i = STPIf(member['name'], vrf=bridge['vrf']) | |
# configure ARP cache timeout | |
i.set_arp_cache_tmo(bridge['arp_cache_tmo']) | |
# ignore link state changes | |
diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py | |
index eb0145f..01ec61e 100755 | |
--- a/src/conf_mode/interfaces-dummy.py | |
+++ b/src/conf_mode/interfaces-dummy.py | |
@@ -30,7 +30,8 @@ default_config_data = { | |
'deleted': False, | |
'description': '', | |
'disable': False, | |
- 'intf': '' | |
+ 'intf': '', | |
+ 'vrf': '', | |
} | |
def get_config(): | |
@@ -69,6 +70,10 @@ def get_config(): | |
act_addr = conf.return_values('address') | |
dummy['address_remove'] = list_diff(eff_addr, act_addr) | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ dummy['vrf'] = conf.return_value('vrf') | |
+ | |
return dummy | |
def verify(dummy): | |
@@ -78,12 +83,12 @@ def generate(dummy): | |
return None | |
def apply(dummy): | |
- d = DummyIf(dummy['intf']) | |
- | |
# Remove dummy interface | |
if dummy['deleted']: | |
+ d = DummyIf(dummy['intf']) | |
d.remove() | |
else: | |
+ d = DummyIf(dummy['intf'], dummy['vrf']) | |
# update interface description used e.g. within SNMP | |
d.set_alias(dummy['description']) | |
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py | |
index e4f6e5f..874b95a 100755 | |
--- a/src/conf_mode/interfaces-ethernet.py | |
+++ b/src/conf_mode/interfaces-ethernet.py | |
@@ -59,7 +59,8 @@ default_config_data = { | |
'vif_s': [], | |
'vif_s_remove': [], | |
'vif': [], | |
- 'vif_remove': [] | |
+ 'vif_remove': [], | |
+ 'vrf': '', | |
} | |
def get_config(): | |
@@ -225,6 +226,12 @@ def get_config(): | |
conf.set_level(cfg_base + ['vif', vif]) | |
eth['vif'].append(vlan_to_dict(conf)) | |
+ # re-set configuration level to parse new nodes | |
+ conf.set_level(cfg_base) | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ eth['vrf'] = conf.return_value('vrf') | |
+ | |
return eth | |
@@ -271,11 +278,12 @@ def generate(eth): | |
return None | |
def apply(eth): | |
- e = EthernetIf(eth['intf']) | |
if eth['deleted']: | |
+ e = EthernetIf(eth['intf']) | |
# delete interface | |
e.remove() | |
else: | |
+ e = EthernetIf(eth['intf'], eth['vrf']) | |
# update interface description used e.g. within SNMP | |
e.set_alias(eth['description']) | |
@@ -373,7 +381,8 @@ def apply(eth): | |
# create service VLAN interfaces (vif-s) | |
for vif_s in eth['vif_s']: | |
- s_vlan = e.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) | |
+ s_vlan = e.add_vlan( | |
+ vif_s['id'], ethertype=vif_s['ethertype'], vrf=eth['vrf']) | |
apply_vlan_config(s_vlan, vif_s) | |
# remove no longer required client VLAN interfaces (vif-c) | |
@@ -384,7 +393,7 @@ def apply(eth): | |
# create client VLAN interfaces (vif-c) | |
# on lower service VLAN interface | |
for vif_c in vif_s['vif_c']: | |
- c_vlan = s_vlan.add_vlan(vif_c['id']) | |
+ c_vlan = s_vlan.add_vlan(vif_c['id'], vrf=eth['vrf']) | |
apply_vlan_config(c_vlan, vif_c) | |
# remove no longer required VLAN interfaces (vif) | |
@@ -403,7 +412,7 @@ def apply(eth): | |
except: | |
pass | |
- vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos']) | |
+ vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos'], vrf=eth['vrf']) | |
apply_vlan_config(vlan, vif) | |
return None | |
diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py | |
index b0c3816..5ba85a9 100755 | |
--- a/src/conf_mode/interfaces-geneve.py | |
+++ b/src/conf_mode/interfaces-geneve.py | |
@@ -34,7 +34,8 @@ default_config_data = { | |
'ip_proxy_arp': 0, | |
'mtu': 1500, | |
'remote': '', | |
- 'vni': '' | |
+ 'vni': '', | |
+ 'vrf': '', | |
} | |
def get_config(): | |
@@ -87,6 +88,10 @@ def get_config(): | |
if conf.exists('vni'): | |
geneve['vni'] = conf.return_value('vni') | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ geneve['vrf'] = conf.return_value('vrf') | |
+ | |
return geneve | |
@@ -127,7 +132,7 @@ def apply(geneve): | |
conf['remote'] = geneve['remote'] | |
# Finally create the new interface | |
- g = GeneveIf(geneve['intf'], config=conf) | |
+ g = GeneveIf(geneve['intf'], config=conf, vrf=geneve['vrf']) | |
# update interface description used e.g. by SNMP | |
g.set_alias(geneve['description']) | |
# Maximum Transfer Unit (MTU) | |
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py | |
index ae49dad..b4da968 100755 | |
--- a/src/conf_mode/interfaces-l2tpv3.py | |
+++ b/src/conf_mode/interfaces-l2tpv3.py | |
@@ -39,7 +39,8 @@ default_config_data = { | |
'remote_address': '', | |
'remote_port': 5000, | |
'session_id': '', | |
- 'tunnel_id': '' | |
+ 'tunnel_id': '', | |
+ 'vrf': '', | |
} | |
def get_config(): | |
@@ -122,6 +123,10 @@ def get_config(): | |
if conf.exists('tunnel-id'): | |
l2tpv3['tunnel_id'] = conf.return_value('tunnel-id') | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ l2tpv3['vrf'] = conf.return_value('vrf') | |
+ | |
return l2tpv3 | |
@@ -208,7 +213,7 @@ def apply(l2tpv3): | |
conf['peer_session_id'] = l2tpv3['peer_session_id'] | |
# Finally create the new interface | |
- l = L2TPv3If(l2tpv3['intf'], config=conf) | |
+ l = L2TPv3If(l2tpv3['intf'], config=conf, vrf=l2tpv3['vrf']) | |
# update interface description used e.g. by SNMP | |
l.set_alias(l2tpv3['description']) | |
# Maximum Transfer Unit (MTU) | |
diff --git a/src/conf_mode/interfaces-loopback.py b/src/conf_mode/interfaces-loopback.py | |
index 10722d1..ca4edc5 100755 | |
--- a/src/conf_mode/interfaces-loopback.py | |
+++ b/src/conf_mode/interfaces-loopback.py | |
@@ -29,6 +29,7 @@ default_config_data = { | |
'address_remove': [], | |
'deleted': False, | |
'description': '', | |
+ 'vrf': '', | |
} | |
@@ -63,6 +64,10 @@ def get_config(): | |
act_addr = conf.return_values('address') | |
loopback['address_remove'] = list_diff(eff_addr, act_addr) | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ loopback['vrf'] = conf.return_value('vrf') | |
+ | |
return loopback | |
def verify(loopback): | |
@@ -72,10 +77,11 @@ def generate(loopback): | |
return None | |
def apply(loopback): | |
- l = LoopbackIf(loopback['intf']) | |
if loopback['deleted']: | |
+ l = LoopbackIf(loopback['intf']) | |
l.remove() | |
else: | |
+ l = LoopbackIf(loopback['intf'], vrf=loopback['vrf']) | |
# update interface description used e.g. within SNMP | |
l.set_alias(loopback['description']) | |
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py | |
index 3a7bc66..20fffd4 100755 | |
--- a/src/conf_mode/interfaces-openvpn.py | |
+++ b/src/conf_mode/interfaces-openvpn.py | |
@@ -324,6 +324,7 @@ default_config_data = { | |
'type': 'tun', | |
'uid': user, | |
'gid': group, | |
+ 'vrf': '', | |
} | |
def subprocess_cmd(command): | |
@@ -649,6 +650,10 @@ def get_config(): | |
if conf.exists('use-lzo-compression'): | |
openvpn['compress_lzo'] = True | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ openvpn['vrf'] = conf.return_value('vrf') | |
+ | |
return openvpn | |
def verify(openvpn): | |
@@ -991,14 +996,14 @@ def apply(openvpn): | |
try: | |
# we need to catch the exception if the interface is not up due to | |
# reason stated above | |
- Interface(openvpn['intf']).set_alias(openvpn['description']) | |
+ Interface(openvpn['intf'], vrf=openvpn['vrf']).set_alias(openvpn['description']) | |
except: | |
pass | |
# TAP interface needs to be brought up explicitly | |
if openvpn['type'] == 'tap': | |
if not openvpn['disable']: | |
- Interface(openvpn['intf']).set_state('up') | |
+ Interface(openvpn['intf'], vrf=openvpn['vrf']).set_state('up') | |
return None | |
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py | |
index efdc21f..70b29a5 100755 | |
--- a/src/conf_mode/interfaces-vxlan.py | |
+++ b/src/conf_mode/interfaces-vxlan.py | |
@@ -42,7 +42,8 @@ default_config_data = { | |
'remote': '', | |
'remote_port': 8472, # The Linux implementation of VXLAN pre-dates | |
# the IANA's selection of a standard destination port | |
- 'vni': '' | |
+ 'vni': '', | |
+ 'vrf': '', | |
} | |
def get_config(): | |
@@ -123,6 +124,10 @@ def get_config(): | |
if conf.exists('vni'): | |
vxlan['vni'] = conf.return_value('vni') | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ vxlan['vrf'] = conf.return_value('vrf') | |
+ | |
return vxlan | |
@@ -180,7 +185,7 @@ def apply(vxlan): | |
conf['port'] = vxlan['remote_port'] | |
# Finally create the new interface | |
- v = VXLANIf(vxlan['intf'], config=conf) | |
+ v = VXLANIf(vxlan['intf'], config=conf, vrf=vxlan['vrf']) | |
# update interface description used e.g. by SNMP | |
v.set_alias(vxlan['description']) | |
# Maximum Transfer Unit (MTU) | |
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py | |
index ff12a51..39d1b1f 100755 | |
--- a/src/conf_mode/interfaces-wireguard.py | |
+++ b/src/conf_mode/interfaces-wireguard.py | |
@@ -68,7 +68,8 @@ def get_config(): | |
'mtu': 1420, | |
'peer': {}, | |
'peer_remove': [], | |
- 'pk': '{}/default/private.key'.format(kdir) | |
+ 'pk': '{}/default/private.key'.format(kdir), | |
+ 'vrf': '', | |
} | |
if os.getenv('VYOS_TAGNODE_VALUE'): | |
@@ -171,6 +172,11 @@ def get_config(): | |
else: | |
peer_key = c.return_value(['peer', p, 'pubkey']) | |
wg['peer_remove'].append(peer_key) | |
+ | |
+ # interface vrf | |
+ if c.exists('vrf'): | |
+ wg['vrf'] = c.return_value('vrf') | |
+ | |
return wg | |
@@ -210,7 +216,7 @@ def apply(c): | |
return None | |
# init wg class | |
- intfc = WireGuardIf(c['intfc']) | |
+ intfc = WireGuardIf(c['intfc'], vrf=c['vrf']) | |
# single interface removal | |
if c['delete']: | |
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py | |
index 162aaf4..835f28f 100755 | |
--- a/src/conf_mode/interfaces-wireless.py | |
+++ b/src/conf_mode/interfaces-wireless.py | |
@@ -836,7 +836,8 @@ default_config_data = { | |
'ssid' : '', | |
'type' : 'monitor', | |
'vif': [], | |
- 'vif_remove': [] | |
+ 'vif_remove': [], | |
+ 'vrf': '', | |
} | |
def get_conf_file(conf_type, intf): | |
@@ -1254,6 +1255,10 @@ def get_config(): | |
if conf.exists('wifi-regulatory-domain'): | |
wifi['country_code'] = conf.return_value('wifi-regulatory-domain') | |
+ # interface vrf | |
+ if conf.exists('vrf'): | |
+ wifi['vrf'] = conf.return_value('vrf') | |
+ | |
return wifi | |
@@ -1345,11 +1350,12 @@ def generate(wifi): | |
return None | |
def apply(wifi): | |
- w = EthernetIf(wifi['intf']) | |
if wifi['deleted']: | |
+ w = EthernetIf(wifi['intf']]) | |
# delete interface | |
w.remove() | |
else: | |
+ w = EthernetIf(wifi['intf'], wifi['vrf']) | |
# Some parts e.g. MAC address can't be changed when interface is up | |
w.set_state('down') | |
@@ -1430,7 +1436,7 @@ def apply(wifi): | |
except: | |
pass | |
- vlan = e.add_vlan(vif['id']) | |
+ vlan = e.add_vlan(vif['id'], vrf=wifi['vrf']) | |
apply_vlan_config(vlan, vif) | |
# Physical interface is now configured. Proceed by starting hostapd or | |
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py | |
new file mode 100755 | |
index 0000000..9896c7c | |
--- /dev/null | |
+++ b/src/conf_mode/vrf.py | |
@@ -0,0 +1,186 @@ | |
+#!/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 vyos.config import Config | |
+from vyos import ConfigError | |
+from vyos import vrf | |
+ | |
+ | |
+# https://github.com/torvalds/linux/blob/master/Documentation/networking/vrf.txt | |
+ | |
+ | |
+def sysctl(name, value): | |
+ os.system('sysctl -wq {}={}'.format(name, value)) | |
+ | |
+def interfaces_with_vrf (match, effective): | |
+ matched = [] | |
+ config = Config() | |
+ section = config.get_config_dict('interfaces', effective) | |
+ for type in section: | |
+ interfaces = section[type] | |
+ for name in interfaces: | |
+ interface = interfaces[name] | |
+ if 'vrf' in interface: | |
+ v = interface.get('vrf', '') | |
+ if v == match: | |
+ matched.append(name) | |
+ return matched | |
+ | |
+def get_config(): | |
+ command = { | |
+ 'bind':{}, | |
+ 'vrf':[], | |
+ 'int': {}, # per vrf name list of interfaces which will have it | |
+ } | |
+ | |
+ config = Config() | |
+ | |
+ old = {} | |
+ new = {} | |
+ | |
+ if config.exists_effective('vrf'): | |
+ old = deepcopy(config.get_config_dict('vrf', True)) | |
+ | |
+ if config.exists('vrf'): | |
+ new = deepcopy(config.get_config_dict('vrf', False)) | |
+ | |
+ integer = lambda _: '1' if _ else '0' | |
+ command['bind']['ipv4'] = integer('ipv4' not in new.get('disable-bind-to-all', {})) | |
+ command['bind']['ipv6'] = integer('ipv6' not in new.get('disable-bind-to-all', {})) | |
+ | |
+ old_names = old.get('name', []) | |
+ new_names = new.get('name', []) | |
+ all_names = list(set(old_names) | set(new_names)) | |
+ del_names = list(set(old_names).difference(new_names)) | |
+ mod_names = list(set(old_names).intersection(new_names)) | |
+ add_names = list(set(new_names).difference(old_names)) | |
+ | |
+ for name in all_names: | |
+ v = { | |
+ 'name': name, | |
+ 'action': 'miss', | |
+ 'table': -1, | |
+ 'check': -1, | |
+ } | |
+ | |
+ if name in new_names: | |
+ v['table'] = new.get('name', {}).get(name, {}).get('table', -1) | |
+ v['check'] = old.get('name', {}).get(name, {}).get('table', -1) | |
+ | |
+ if name in add_names: | |
+ v['action'] = 'add' | |
+ elif name in del_names: | |
+ v['action'] = 'delete' | |
+ elif name in mod_names: | |
+ if v['table'] != -1: | |
+ if v['check'] == -1: | |
+ v['action'] = 'add' | |
+ else: | |
+ v['action'] = 'modify' | |
+ | |
+ command['vrf'].append(v) | |
+ | |
+ for v in vrf.list_vrfs(): | |
+ name = v['ifname'] | |
+ command['int'][name] = interfaces_with_vrf(name,False) | |
+ | |
+ return command | |
+ | |
+ | |
+def verify(command): | |
+ for v in command['vrf']: | |
+ action = v['action'] | |
+ name = v['name'] | |
+ if action == 'modify' and v['table'] != v['check']: | |
+ raise ConfigError(f'set vrf name {name}: modification of vrf table is not supported yet') | |
+ if action == 'delete' and name in command['int']: | |
+ interface = ', '.join(command['int'][name]) | |
+ if interface: | |
+ raise ConfigError(f'delete vrf name {name}: can not delete vrf as it is used on {interface}') | |
+ | |
+ return command | |
+ | |
+ | |
+def generate(command): | |
+ return command | |
+ | |
+ | |
+def apply(command): | |
+ # set the default VRF global behaviour | |
+ sysctl('net.ipv4.tcp_l3mdev_accept', command['bind']['ipv4']) | |
+ sysctl('net.ipv4.udp_l3mdev_accept', command['bind']['ipv4']) | |
+ | |
+ errors = [] | |
+ for v in command['vrf']: | |
+ name = v['name'] | |
+ action = v['action'] | |
+ table = v['table'] | |
+ | |
+ errors.append(f'could not {action} vrf {name}') | |
+ | |
+ if action == 'miss': | |
+ continue | |
+ | |
+ if action == 'delete': | |
+ if os.system(f'sudo ip link delete dev {name}'): | |
+ continue | |
+ errors.pop() | |
+ continue | |
+ | |
+ if action == 'modify': | |
+ # > uname -a | |
+ # Linux vyos 4.19.101-amd64-vyos #1 SMP Sun Feb 2 10:18:07 UTC 2020 x86_64 GNU/Linux | |
+ # > ip link add my-vrf type vrf table 100 | |
+ # > ip link set my-vrf type vrf table 200 | |
+ # RTNETLINK answers: Operation not supported | |
+ # so require to remove vrf and change all existing the interfaces | |
+ | |
+ if os.system(f'sudo ip link delete dev {name}'): | |
+ continue | |
+ action = 'add' | |
+ | |
+ if action == 'add': | |
+ commands = [ | |
+ f'sudo ip link add {name} type vrf table {table}', | |
+ f'sudo ip link set dev {name} up', | |
+ f'sudo ip -4 rule add oif {name} lookup {table}', | |
+ f'sudo ip -4 rule add iif {name} lookup {table}', | |
+ f'sudo ip -6 rule add oif {name} lookup {table}', | |
+ f'sudo ip -6 rule add iif {name} lookup {table}', | |
+ ] | |
+ | |
+ for command in commands: | |
+ if os.system(command): | |
+ errors[-1] += ' ('+command+')' | |
+ continue | |
+ errors.pop() | |
+ | |
+ if errors: | |
+ raise ConfigError(', '.join(errors)) | |
+ | |
+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/validators/interface-name b/src/validators/interface-name | |
new file mode 100755 | |
index 0000000..49a833f | |
--- /dev/null | |
+++ b/src/validators/interface-name | |
@@ -0,0 +1,29 @@ | |
+#!/usr/bin/env python3 | |
+# | |
+# Copyright (C) 2018 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 re | |
+ | |
+if len(sys.argv) == 2: | |
+ # https://unix.stackexchange.com/questions/451368/allowed-chars-in-linux-network-interface-names | |
+ pattern = "^([^/\s]{1,16}$)$" | |
+ if re.match(pattern, sys.argv[1]): | |
+ sys.exit(0) | |
+ else: | |
+ sys.exit(1) | |
+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment