diff options
-rw-r--r-- | data/op-mode-standardized.json | 1 | ||||
-rw-r--r-- | data/templates/accel-ppp/config_ip_pool.j2 | 12 | ||||
-rw-r--r-- | data/templates/accel-ppp/pppoe.config.j2 | 13 | ||||
-rw-r--r-- | interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i | 18 | ||||
-rw-r--r-- | interface-definitions/include/interface/authentication.xml.i | 8 | ||||
-rw-r--r-- | interface-definitions/service-ipoe-server.xml.in | 17 | ||||
-rw-r--r-- | interface-definitions/service-pppoe-server.xml.in | 1 | ||||
-rw-r--r-- | op-mode-definitions/nhrp.xml.in | 4 | ||||
-rw-r--r-- | python/vyos/configverify.py | 15 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 4 | ||||
-rw-r--r-- | python/vyos/template.py | 20 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_pppoe-server.py | 29 | ||||
-rwxr-xr-x | src/op_mode/nhrp.py | 101 |
13 files changed, 219 insertions, 24 deletions
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json index abf562984..3b2599790 100644 --- a/data/op-mode-standardized.json +++ b/data/op-mode-standardized.json @@ -14,6 +14,7 @@ "memory.py", "nat.py", "neighbor.py", +"nhrp.py", "openconnect.py", "openvpn.py", "route.py", diff --git a/data/templates/accel-ppp/config_ip_pool.j2 b/data/templates/accel-ppp/config_ip_pool.j2 index 0bef4ad69..f7511e445 100644 --- a/data/templates/accel-ppp/config_ip_pool.j2 +++ b/data/templates/accel-ppp/config_ip_pool.j2 @@ -11,4 +11,14 @@ gw-ip-address={{ gateway_address }} {{ subnet }} {% endfor %} {% endif %} -{% endif %} +{% if client_ip_pool.name is vyos_defined %} +{% for pool, pool_config in client_ip_pool.name.items() %} +{% if pool_config.subnet is vyos_defined %} +{{ pool_config.subnet }},name={{ pool }} +{% endif %} +{% if pool_config.gateway_address is vyos_defined %} +gw-ip-address={{ pool_config.gateway_address }} +{% endif %} +{% endfor %} +{% endif %} +{% endif %}
\ No newline at end of file diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2 index c536bafa5..811c4ccc0 100644 --- a/data/templates/accel-ppp/pppoe.config.j2 +++ b/data/templates/accel-ppp/pppoe.config.j2 @@ -133,6 +133,19 @@ pado-delay={{ pado_delay_param.value }} called-sid={{ authentication.radius.called_sid_format }} {% endif %} +{% if authentication.mode is vyos_defined("local") %} +{% if client_ip_pool.name is vyos_defined %} +{% for pool, pool_config in client_ip_pool.name.items() %} +{% if pool_config.subnet is vyos_defined %} +ip-pool={{ pool }} +{% endif %} +{% if pool_config.gateway_address is vyos_defined %} +gw-ip-address={{ pool_config.gateway_address }}/{{ pool_config.subnet.split('/')[1] }} +{% endif %} +{% endfor %} +{% endif %} +{% endif %} + {% if limits is vyos_defined %} [connlimit] {% if limits.connection_limit is vyos_defined %} diff --git a/interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i b/interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i new file mode 100644 index 000000000..654b6727e --- /dev/null +++ b/interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i @@ -0,0 +1,18 @@ +<!-- include start from accel-ppp/client-ip-pool-name.xml.i --> +<tagNode name="name"> + <properties> + <help>Pool name</help> + <valueHelp> + <format>txt</format> + <description>Name of IP pool</description> + </valueHelp> + <constraint> + <regex>[-_a-zA-Z0-9.]+</regex> + </constraint> + </properties> + <children> + #include <include/accel-ppp/gateway-address.xml.i> + #include <include/accel-ppp/client-ip-pool-subnet-single.xml.i> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/authentication.xml.i b/interface-definitions/include/interface/authentication.xml.i index c097ca9dd..7d8352f2e 100644 --- a/interface-definitions/include/interface/authentication.xml.i +++ b/interface-definitions/include/interface/authentication.xml.i @@ -11,6 +11,10 @@ <format>txt</format> <description>Username used for connection</description> </valueHelp> + <constraint> + <regex>[[:alnum:]][-_#@[:alnum:]]{0,127}</regex> + </constraint> + <constraintErrorMessage>Username is limited to alphanumerical characters, -, _, #, and @ with a total lenght of 128</constraintErrorMessage> </properties> </leafNode> <leafNode name="password"> @@ -20,6 +24,10 @@ <format>txt</format> <description>Password used for connection</description> </valueHelp> + <constraint> + <regex>[[:ascii:]]{1,128}</regex> + </constraint> + <constraintErrorMessage>Password is limited to ASCII characters only, with a total lenght of 128</constraintErrorMessage> </properties> </leafNode> </children> diff --git a/interface-definitions/service-ipoe-server.xml.in b/interface-definitions/service-ipoe-server.xml.in index d778f9de0..ca4929249 100644 --- a/interface-definitions/service-ipoe-server.xml.in +++ b/interface-definitions/service-ipoe-server.xml.in @@ -108,22 +108,7 @@ <help>Client IP pools and gateway setting</help> </properties> <children> - <tagNode name="name"> - <properties> - <help>Pool name</help> - <valueHelp> - <format>txt</format> - <description>Name of IP pool</description> - </valueHelp> - <constraint> - <regex>[-_a-zA-Z0-9.]+</regex> - </constraint> - </properties> - <children> - #include <include/accel-ppp/gateway-address.xml.i> - #include <include/accel-ppp/client-ip-pool-subnet-single.xml.i> - </children> - </tagNode> + #include <include/accel-ppp/client-ip-pool-name.xml.i> </children> </node> #include <include/accel-ppp/client-ipv6-pool.xml.i> diff --git a/interface-definitions/service-pppoe-server.xml.in b/interface-definitions/service-pppoe-server.xml.in index 65e0f41c4..3fde07019 100644 --- a/interface-definitions/service-pppoe-server.xml.in +++ b/interface-definitions/service-pppoe-server.xml.in @@ -56,6 +56,7 @@ <children> #include <include/accel-ppp/client-ip-pool-start-stop.xml.i> #include <include/accel-ppp/client-ip-pool-subnet.xml.i> + #include <include/accel-ppp/client-ip-pool-name.xml.i> </children> </node> #include <include/accel-ppp/client-ipv6-pool.xml.i> diff --git a/op-mode-definitions/nhrp.xml.in b/op-mode-definitions/nhrp.xml.in index c10b111a7..11a4b8814 100644 --- a/op-mode-definitions/nhrp.xml.in +++ b/op-mode-definitions/nhrp.xml.in @@ -50,13 +50,13 @@ <properties> <help>Show NHRP interface connection information</help> </properties> - <command>if pgrep opennhrp >/dev/null; then sudo opennhrpctl interface show; else echo OpenNHRP is not running; fi</command> + <command>${vyos_op_scripts_dir}/nhrp.py show_interface</command> </leafNode> <leafNode name="tunnel"> <properties> <help>Show NHRP tunnel connection information</help> </properties> - <command>if pgrep opennhrp >/dev/null; then sudo opennhrpctl show ; else echo OpenNHRP is not running; fi</command> + <command>${vyos_op_scripts_dir}/nhrp.py show_tunnel</command> </leafNode> </children> </node> diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 8e0ce701e..63edacc81 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-2023 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 @@ -23,6 +23,7 @@ from vyos import ConfigError from vyos.util import dict_search +from vyos.util import dict_search_recursive def verify_mtu(config): """ @@ -414,7 +415,17 @@ def verify_accel_ppp_base_service(config, local_users=True): if 'key' not in radius_config: raise ConfigError(f'Missing RADIUS secret key for server "{server}"') - if 'gateway_address' not in config: + # Check global gateway or gateway in named pool + gateway = False + if 'gateway_address' in config: + gateway = True + else: + if dict_search_recursive(config, 'gateway_address', ['client_ip_pool', 'name']): + for _, v in config['client_ip_pool']['name'].items(): + if 'gateway_address' in v: + gateway = True + break + if not gateway: raise ConfigError('Server requires gateway-address to be configured!') if 'name_server_ipv4' in config: diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index c50ead89f..795df2462 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -751,8 +751,8 @@ class Interface(Control): elif all_rp_filter == 2: global_setting = 'loose' from vyos.base import Warning - Warning(f'Global source-validation is set to "{global_setting} '\ - f'this overrides per interface setting!') + Warning(f'Global source-validation is set to "{global_setting}", this '\ + f'overrides per interface setting on "{self.ifname}"!') tmp = self.get_interface('rp_filter') if int(tmp) == value: diff --git a/python/vyos/template.py b/python/vyos/template.py index 15240f815..6367f51e5 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -1,4 +1,4 @@ -# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2023 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 @@ -158,6 +158,24 @@ def force_to_list(value): else: return [value] +@register_filter('seconds_to_human') +def seconds_to_human(seconds, separator=""): + """ Convert seconds to human-readable values like 1d6h15m23s """ + from vyos.util import seconds_to_human + return seconds_to_human(seconds, separator=separator) + +@register_filter('bytes_to_human') +def bytes_to_human(bytes, initial_exponent=0, precision=2): + """ Convert bytes to human-readable values like 1.44M """ + from vyos.util import bytes_to_human + return bytes_to_human(bytes, initial_exponent=initial_exponent, precision=precision) + +@register_filter('human_to_bytes') +def human_to_bytes(value): + """ Convert a data amount with a unit suffix to bytes, like 2K to 2048 """ + from vyos.util import human_to_bytes + return human_to_bytes(value) + @register_filter('ip_from_cidr') def ip_from_cidr(prefix): """ Take an IPv4/IPv6 CIDR host and strip cidr mask. diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 0b442067c..4f9181704 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -171,6 +171,35 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.assertEqual(conf['shaper']['down-limiter'], limiter) + def test_pppoe_server_client_ip_pool_name(self): + # Test configuration of named client pools + self.basic_config() + + subnet = '192.0.2.0/24' + gateway = '192.0.2.1' + pool = 'VYOS' + + subnet_name = f'{subnet},name' + gw_ip_prefix = f'{gateway}/24' + + self.set(['client-ip-pool', 'name', pool, 'subnet', subnet]) + self.set(['client-ip-pool', 'name', pool, 'gateway-address', gateway]) + self.cli_delete(self._base_path + ['gateway-address']) + + # commit changes + self.cli_commit() + + # Validate configuration values + conf = ConfigParser(allow_no_value=True, delimiters='=') + conf.read(self._config_file) + + # Validate configuration + self.assertEqual(conf['ip-pool'][subnet_name], pool) + self.assertEqual(conf['ip-pool']['gw-ip-address'], gateway) + self.assertEqual(conf['pppoe']['ip-pool'], pool) + self.assertEqual(conf['pppoe']['gw-ip-address'], gw_ip_prefix) + + def test_pppoe_server_client_ipv6_pool(self): # Test configuration of IPv6 client pools self.basic_config() diff --git a/src/op_mode/nhrp.py b/src/op_mode/nhrp.py new file mode 100755 index 000000000..5ff91a59c --- /dev/null +++ b/src/op_mode/nhrp.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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 tabulate +import vyos.opmode + +from vyos.util import cmd +from vyos.util import process_named_running +from vyos.util import colon_separated_to_dict + + +def _get_formatted_output(output_dict: dict) -> str: + """ + Create formatted table for CLI output + :param output_dict: dictionary for API + :type output_dict: dict + :return: tabulate string + :rtype: str + """ + print(f"Status: {output_dict['Status']}") + output: str = tabulate.tabulate(output_dict['routes'], headers='keys', + numalign="left") + return output + + +def _get_formatted_dict(output_string: str) -> dict: + """ + Format string returned from CMD to API list + :param output_string: String received by CMD + :type output_string: str + :return: dictionary for API + :rtype: dict + """ + formatted_dict: dict = { + 'Status': '', + 'routes': [] + } + output_list: list = output_string.split('\n\n') + for list_a in output_list: + output_dict = colon_separated_to_dict(list_a, True) + if 'Status' in output_dict: + formatted_dict['Status'] = output_dict['Status'] + else: + formatted_dict['routes'].append(output_dict) + return formatted_dict + + +def show_interface(raw: bool): + """ + Command 'show nhrp interface' + :param raw: if API + :type raw: bool + """ + if not process_named_running('opennhrp'): + raise vyos.opmode.UnconfiguredSubsystem('OpenNHRP is not running.') + interface_string: str = cmd('sudo opennhrpctl interface show') + interface_dict: dict = _get_formatted_dict(interface_string) + if raw: + return interface_dict + else: + return _get_formatted_output(interface_dict) + + +def show_tunnel(raw: bool): + """ + Command 'show nhrp tunnel' + :param raw: if API + :type raw: bool + """ + if not process_named_running('opennhrp'): + raise vyos.opmode.UnconfiguredSubsystem('OpenNHRP is not running.') + tunnel_string: str = cmd('sudo opennhrpctl show') + tunnel_dict: list = _get_formatted_dict(tunnel_string) + if raw: + return tunnel_dict + else: + return _get_formatted_output(tunnel_dict) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) |