diff options
-rw-r--r-- | data/templates/frr/bgp.frr.tmpl | 4 | ||||
-rw-r--r-- | data/templates/openvpn/server.conf.tmpl | 24 | ||||
-rw-r--r-- | interface-definitions/include/interface-mirror.xml.i | 26 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 49 | ||||
-rw-r--r-- | smoketest/scripts/cli/base_interfaces_test.py | 34 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bgp.py | 44 | ||||
-rwxr-xr-x | src/migration-scripts/interfaces/16-to-17 | 52 |
7 files changed, 179 insertions, 54 deletions
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl index 86e1aa366..16355a1e5 100644 --- a/data/templates/frr/bgp.frr.tmpl +++ b/data/templates/frr/bgp.frr.tmpl @@ -114,9 +114,9 @@ {% endif %} {% if config.address_family[af].prefix_list is defined and config.address_family[af].prefix_list is not none %} {% if config.address_family[af].prefix_list.export is defined and config.address_family[af].prefix_list.export is not none %} - neighbor {{ neighbor }} route-map {{ config.address_family[af].prefix_list.export }} out + neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.export }} out {% elif config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %} - neighbor {{ neighbor }} route-map {{ config.address_family[af].prefix_list.export }} in + neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.import }} in {% endif %} {% endif %} {% if config.address_family[af].soft_reconfiguration is defined and config.address_family[af].soft_reconfiguration.inbound is defined %} diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index a1daaa078..9fc281afc 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -227,7 +227,29 @@ cipher aes-256-cbc {% endif %} {% endif %} {% if encryption.ncp_ciphers is defined and encryption.ncp_ciphers is not none %} -ncp-ciphers {{ encryption.ncp_ciphers | join(':') }} +{% set cipher_list = [] %} +{% for cipher in encryption.ncp_ciphers %} +{% if cipher == 'none' %} +{% set cipher_list = cipher_list.append('none') %} +{% elif cipher == 'des' %} +{% set cipher_list = cipher_list.append('des-cbc') %} +{% elif cipher == '3des' %} +{% set cipher_list = cipher_list.append('des-ede3-cbc') %} +{% elif cipher == 'aes128' %} +{% set cipher_list = cipher_list.append('aes-128-cbc') %} +{% elif cipher == 'aes128gcm' %} +{% set cipher_list = cipher_list.append('aes-128-gcm') %} +{% elif cipher == 'aes192' %} +{% set cipher_list = cipher_list.append('aes-192-cbc') %} +{% elif cipher == 'aes192gcm' %} +{% set cipher_list = cipher_list.append('aes-192-gcm') %} +{% elif cipher == 'aes256' %} +{% set cipher_list = cipher_list.append('aes-256-cbc') %} +{% elif cipher == 'aes256gcm' %} +{% set cipher_list = cipher_list.append('aes-256-gcm') %} +{% endif %} +{% endfor %} +ncp-ciphers {{ cipher_list | join(':') }}:{{ cipher_list | join(':') | upper }} {% elif encryption.disable_ncp is defined %} ncp-disable {% endif %} diff --git a/interface-definitions/include/interface-mirror.xml.i b/interface-definitions/include/interface-mirror.xml.i index e3720cde7..d34132a9c 100644 --- a/interface-definitions/include/interface-mirror.xml.i +++ b/interface-definitions/include/interface-mirror.xml.i @@ -1,11 +1,25 @@ <!-- included start from interface-mirror.xml.i --> -<leafNode name="mirror"> +<node name="mirror"> <properties> <help>Incoming/outgoing packet mirroring destination</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - <multi/> </properties> -</leafNode> + <children> + <leafNode name="ingress"> + <properties> + <help>Mirror the ingress traffic of the interface to the destination interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + <leafNode name="egress"> + <properties> + <help>Mirror the egress traffic of the interface to the destination interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + </children> +</node> <!-- included end --> diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 24f60efb8..6e6a83f36 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -977,25 +977,40 @@ class Interface(Control): old_handle = rule['handle'] old_kind = rule['kind'] if old_dev == dev and old_handle == handle and old_kind == kind: - delete_tc_cmd = f'tc qdisc del dev {dev} handle {handle} {kind}' - self._cmd(delete_tc_cmd) - - - - def apply_mirror(self,config): - ifname = config['ifname'] - + if 'root' in rule and rule['root']: + delete_tc_cmd = f'tc qdisc del dev {dev} handle {handle} root {kind}' + self._cmd(delete_tc_cmd) + else: + delete_tc_cmd = f'tc qdisc del dev {dev} handle {handle} {kind}' + self._cmd(delete_tc_cmd) + + def apply_mirror(self): + # Please refer to the document for details + # https://man7.org/linux/man-pages/man8/tc.8.html + # https://man7.org/linux/man-pages/man8/tc-mirred.8.html + ifname = self._config['ifname'] # Remove existing mirroring rules self.del_tc_qdisc(ifname,'ingress','ffff:') - + self.del_tc_qdisc(ifname,'prio','1:') + # Setting up packet mirroring - mirror = dict_search('mirror', config) - if mirror: - for interface in mirror: - mirror_cmd = f'tc qdisc add dev {ifname} handle ffff: ingress' - self._cmd(mirror_cmd) - mirror_cmd = f'tc filter add dev {ifname} parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {interface}' - self._cmd(mirror_cmd) + ingress_mirror = dict_search('mirror.ingress', self._config) + if ingress_mirror: + # Mirror ingress traffic + mirror_cmd = f'tc qdisc add dev {ifname} handle ffff: ingress' + self._cmd(mirror_cmd) + # Export the mirrored traffic to the interface + mirror_cmd = f'tc filter add dev {ifname} parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {ingress_mirror}' + self._cmd(mirror_cmd) + + egress_mirror = dict_search('mirror.egress', self._config) + if egress_mirror: + # Mirror egress traffic + mirror_cmd = f'tc qdisc add dev {ifname} handle 1: root prio' + self._cmd(mirror_cmd) + # Export the mirrored traffic to the interface + mirror_cmd = f'tc filter add dev {ifname} parent 1: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {egress_mirror}' + self._cmd(mirror_cmd) def update(self, config): """ General helper function which works on a dictionary retrived by @@ -1215,7 +1230,7 @@ class Interface(Control): vlan = VLANIf(vif_ifname, **tmp) vlan.update(vif_config) - self.apply_mirror(config) + self.apply_mirror() diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 4187fd77c..d1bb9c3fe 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -28,6 +28,22 @@ from vyos.util import dict_search from vyos.validate import is_intf_addr_assigned from vyos.validate import is_ipv6_link_local +def read_mirror_rule(interfaces): + Success = 0 + for interface in interfaces: + get_tc_cmd = 'tc -j qdisc' + tmp = cmd(get_tc_cmd, shell=True) + data = json.loads(tmp) + for rule in data: + dev = rule['dev'] + handle = rule['handle'] + kind = rule['kind'] + if dev == interface and handle == "ffff:" and kind == "ingress": + Success+=1 + elif dev == interface and handle == "1:" and kind == "prio": + Success+=1 + return Success + class BasicInterfaceTest: class BaseTest(unittest.TestCase): _test_ip = False @@ -73,25 +89,19 @@ class BasicInterfaceTest: Success = 0 i = 0 if self._test_mirror: + # Check the two-way mirror rules of ingress and egress for interface in self._interfaces: - self.session.set(self._base_path + [interface, 'mirror', 'lo']) + self.session.set(self._base_path + [interface, 'mirror', 'ingress', 'lo']) + self.session.set(self._base_path + [interface, 'mirror', 'egress', 'lo']) i+=1 self.session.commit() # Parse configuration - for interface in self._interfaces: - get_tc_cmd = 'tc -j qdisc' - tmp = cmd(get_tc_cmd, shell=True) - data = json.loads(tmp) - for rule in data: - dev = rule['dev'] - handle = rule['handle'] - kind = rule['kind'] - if dev == interface and handle == "ffff:" and kind == "ingress": - Success+=1 - if Success == i: + Success = read_mirror_rule(self._interfaces) + if Success == i*2: self.assertTrue(True) else: self.assertTrue(False) + i=0 else: return None diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 642738b09..a3f32fd2d 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -60,12 +60,22 @@ def verify(bgp): if neigh not in asn_config: continue - for neighbor, config in asn_config[neigh].items(): - if 'remote_as' not in config and 'peer_group' not in config: - raise ConfigError(f'BGP remote-as must be specified for "{neighbor}"!') + #for neighbor, config in asn_config[neigh].items(): + ''' + # These checks need to be modified. Because peer-group can be declared without 'remote-as'. + # When 'remote-as' configured for specific neighbor in peer-group. For example + # - if 'remote_as' in config and 'peer_group' in config: - raise ConfigError(f'BGP peer-group member "{neighbor}" cannot override remote-as of peer-group!') + set protocols nbgp 65001 neighbor 100.64.0.2 peer-group 'FOO' + set protocols nbgp 65001 neighbor 100.64.0.2 remote-as '65002' + set protocols nbgp 65001 peer-group FOO + + ''' + #if 'remote_as' not in config and 'peer_group' not in config: + # raise ConfigError(f'BGP remote-as must be specified for "{neighbor}"!') + + #if 'remote_as' in config and 'peer_group' in config: + # raise ConfigError(f'BGP peer-group member "{neighbor}" cannot override remote-as of peer-group!') return None @@ -87,24 +97,26 @@ def generate(bgp): def apply(bgp): # Save original configuration prior to starting any commit actions - frr_cfg = {} - frr_cfg['original_config'] = frr.get_configuration(daemon='bgpd') - frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], bgp['new_frr_config'], from_re='router bgp .*') + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(daemon='bgpd') + frr_cfg.modify_section(f'router bgp \S+', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config']) + frr_cfg.commit_configuration(daemon='bgpd') + + # If FRR config is blank, rerun the blank commit x times due to frr-reload + # behavior/bug not properly clearing out on one commit. + if bgp['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(daemon='bgpd') # Debugging + ''' print('') print('--------- DEBUGGING ----------') print(f'Existing config:\n{frr_cfg["original_config"]}\n\n') print(f'Replacement config:\n{bgp["new_frr_config"]}\n\n') print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n') - - # FRR mark configuration will test for syntax errors and throws an - # exception if any syntax errors is detected - frr.mark_configuration(frr_cfg['modified_config']) - - # Commit resulting configuration to FRR, this will throw CommitError - # on failure - frr.reload_configuration(frr_cfg['modified_config'], daemon='bgpd') + ''' return None diff --git a/src/migration-scripts/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17 new file mode 100755 index 000000000..a6b4c7663 --- /dev/null +++ b/src/migration-scripts/interfaces/16-to-17 @@ -0,0 +1,52 @@ +#!/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/>. + +# Command line migration of port mirroring +# https://phabricator.vyos.net/T3089 + +import sys +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(sys.argv) < 1): + 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) + base = ['interfaces', 'ethernet'] + if not config.exists(base): + # Nothing to do + sys.exit(0) + + for interface in config.list_nodes(base): + mirror_old_base = base + [interface, 'mirror'] + if config.exists(mirror_old_base): + intf = config.return_values(mirror_old_base) + if config.exists(mirror_old_base): + config.delete(mirror_old_base) + config.set(mirror_old_base + ['ingress'],intf[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) |