diff options
-rw-r--r-- | data/templates/frr/bgp.frr.tmpl | 4 | ||||
-rw-r--r-- | interface-definitions/system-ip.xml.in | 3 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 13 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_system_ip.py | 79 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bgp.py | 44 | ||||
-rwxr-xr-x | src/conf_mode/system-ip.py | 79 |
6 files changed, 159 insertions, 63 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/interface-definitions/system-ip.xml.in b/interface-definitions/system-ip.xml.in index 14b3b8a07..0bd461042 100644 --- a/interface-definitions/system-ip.xml.in +++ b/interface-definitions/system-ip.xml.in @@ -15,7 +15,7 @@ <children> <leafNode name="table-size"> <properties> - <help>Maximum number of entries to keep in the ARP cache</help> + <help>Maximum number of entries to keep in the ARP cache (default: 8192)</help> <completionHelp> <list>1024 2048 4096 8192 16384 32768</list> </completionHelp> @@ -23,6 +23,7 @@ <regex>(1024|2048|4096|8192|16384|32768)</regex> </constraint> </properties> + <defaultValue>8192</defaultValue> </leafNode> </children> </node> diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index b8c962575..6e6a83f36 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -40,6 +40,7 @@ from vyos.util import read_file from vyos.template import is_ipv4 from vyos.template import is_ipv6 from vyos.validate import is_intf_addr_assigned +from vyos.validate import is_ipv6_link_local from vyos.validate import assert_boolean from vyos.validate import assert_list from vyos.validate import assert_mac @@ -564,7 +565,6 @@ class Interface(Control): prefixlen = prefix.split('/')[1] self.del_addr(f'{eui64}/{prefixlen}') - def set_ipv6_forwarding(self, forwarding): """ Configure IPv6 interface-specific Host/Router behaviour. @@ -1057,7 +1057,14 @@ class Interface(Control): # list of addresses which are no longer in the dict so they can be removed cur_addr = self.get_addr() for addr in list_diff(cur_addr, new_addr): - self.del_addr(addr) + # we will delete all interface specific IP addresses if they are not + # explicitly configured on the CLI + if is_ipv6_link_local(addr): + eui64 = mac2eui64(self.get_mac(), 'fe80::/64') + if addr != f'{eui64}/64': + self.del_addr(addr) + else: + self.del_addr(addr) for addr in new_addr: self.add_addr(addr) @@ -1157,7 +1164,7 @@ class Interface(Control): tmp = dict_search('ipv6.address.no_default_link_local', config) # we must check explicitly for None type as if the key is set we will # get an empty dict (<class 'dict'>) - if tmp is not None: + if isinstance(tmp, dict): self.del_ipv6_eui64_address('fe80::/64') else: self.add_ipv6_eui64_address('fe80::/64') diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py new file mode 100755 index 000000000..4fcaaa465 --- /dev/null +++ b/smoketest/scripts/cli/test_system_ip.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 Francois Mertz fireboxled@gmail.com +# +# 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 +import unittest + +from vyos.configsession import ConfigSession +from vyos.util import read_file + +base_path = ['system', 'ip'] + +class TestSystemIP(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + self.session.delete(base_path) + self.session.commit() + del self.session + + def test_system_ip_forwarding(self): + """ Test if IPv4 forwarding can be disabled globally, default is '1' + which means forwearding enabled """ + all_forwarding = '/proc/sys/net/ipv4/conf/all/forwarding' + self.assertEqual(read_file(all_forwarding), '1') + + self.session.set(base_path + ['disable-forwarding']) + self.session.commit() + + self.assertEqual(read_file(all_forwarding), '0') + + def test_system_ip_multipath(self): + """ test IPv4 multipathing options, options default to off -> '0' """ + use_neigh = '/proc/sys/net/ipv4/fib_multipath_use_neigh' + hash_policy = '/proc/sys/net/ipv4/fib_multipath_hash_policy' + + self.assertEqual(read_file(use_neigh), '0') + self.assertEqual(read_file(hash_policy), '0') + + self.session.set(base_path + ['multipath', 'ignore-unreachable-nexthops']) + self.session.set(base_path + ['multipath', 'layer4-hashing']) + self.session.commit() + + self.assertEqual(read_file(use_neigh), '1') + self.assertEqual(read_file(hash_policy), '1') + + def test_system_ip_arp_table_size(self): + """ Maximum number of entries to keep in the ARP cache, the default is 8k """ + + gc_thresh3 = '/proc/sys/net/ipv4/neigh/default/gc_thresh3' + gc_thresh2 = '/proc/sys/net/ipv4/neigh/default/gc_thresh2' + gc_thresh1 = '/proc/sys/net/ipv4/neigh/default/gc_thresh1' + self.assertEqual(read_file(gc_thresh3), '8192') + self.assertEqual(read_file(gc_thresh2), '4096') + self.assertEqual(read_file(gc_thresh1), '1024') + + for size in [1024, 2048, 4096, 8192, 16384, 32768]: + self.session.set(base_path + ['arp', 'table-size', str(size)]) + self.session.commit() + + self.assertEqual(read_file(gc_thresh3), str(size)) + self.assertEqual(read_file(gc_thresh2), str(size // 2)) + self.assertEqual(read_file(gc_thresh1), str(size // 8)) + +if __name__ == '__main__': + unittest.main() 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/conf_mode/system-ip.py b/src/conf_mode/system-ip.py index 64c9e6d05..190a0daca 100755 --- a/src/conf_mode/system-ip.py +++ b/src/conf_mode/system-ip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -14,68 +14,65 @@ # 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.configdict import dict_merge from vyos.util import call - +from vyos.util import dict_search +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() -default_config_data = { - 'arp_table': 8192, - 'ipv4_forward': '1', - 'mp_unreach_nexthop': '0', - 'mp_layer4_hashing': '0' -} - def sysctl(name, value): - call('sysctl -wq {}={}'.format(name, value)) + call(f'sysctl -wq {name}={value}') def get_config(config=None): - ip_opt = deepcopy(default_config_data) if config: conf = config else: conf = Config() - conf.set_level('system ip') - if conf.exists(''): - if conf.exists('arp table-size'): - ip_opt['arp_table'] = int(conf.return_value('arp table-size')) - - if conf.exists('disable-forwarding'): - ip_opt['ipv4_forward'] = '0' + base = ['system', 'ip'] - if conf.exists('multipath ignore-unreachable-nexthops'): - ip_opt['mp_unreach_nexthop'] = '1' + opt = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + opt = dict_merge(default_values, opt) - if conf.exists('multipath layer4-hashing'): - ip_opt['mp_layer4_hashing'] = '1' + return opt - return ip_opt - -def verify(ip_opt): +def verify(opt): pass -def generate(ip_opt): +def generate(opt): pass -def apply(ip_opt): - # apply ARP threshold values - sysctl('net.ipv4.neigh.default.gc_thresh3', ip_opt['arp_table']) - sysctl('net.ipv4.neigh.default.gc_thresh2', ip_opt['arp_table'] // 2) - sysctl('net.ipv4.neigh.default.gc_thresh1', ip_opt['arp_table'] // 8) +def apply(opt): + size = int(dict_search('arp.table_size', opt)) + if size: + # apply ARP threshold values + sysctl('net.ipv4.neigh.default.gc_thresh3', str(size)) + sysctl('net.ipv4.neigh.default.gc_thresh2', str(size // 2)) + sysctl('net.ipv4.neigh.default.gc_thresh1', str(size // 8)) # enable/disable IPv4 forwarding - with open('/proc/sys/net/ipv4/conf/all/forwarding', 'w') as f: - f.write(ip_opt['ipv4_forward']) - - # configure multipath - sysctl('net.ipv4.fib_multipath_use_neigh', ip_opt['mp_unreach_nexthop']) - sysctl('net.ipv4.fib_multipath_hash_policy', ip_opt['mp_layer4_hashing']) + tmp = '1' + if 'disable_forwarding' in opt: + tmp = '0' + sysctl('net.ipv4.conf.all.forwarding', tmp) + + tmp = '0' + # configure multipath - dict_search() returns an empty dict if key was found + if isinstance(dict_search('multipath.ignore_unreachable_nexthops', opt), dict): + tmp = '1' + sysctl('net.ipv4.fib_multipath_use_neigh', tmp) + + tmp = '0' + if isinstance(dict_search('multipath.ignore_unreachable_nexthops', opt), dict): + tmp = '1' + sysctl('net.ipv4.fib_multipath_hash_policy', tmp) if __name__ == '__main__': try: |