summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/accel-ppp/l2tp.config.j23
-rw-r--r--data/templates/accel-ppp/pptp.config.j23
-rw-r--r--data/templates/high-availability/keepalived.conf.j28
-rw-r--r--interface-definitions/high-availability.xml.in19
-rw-r--r--interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i15
-rw-r--r--interface-definitions/vpn-l2tp.xml.in1
-rw-r--r--python/vyos/utils/dict.py21
-rw-r--r--python/vyos/utils/io.py103
-rwxr-xr-xsmoketest/scripts/system/test_kernel_options.py13
-rwxr-xr-xsrc/conf_mode/high-availability.py7
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py6
-rwxr-xr-xsrc/conf_mode/vpn_pptp.py4
12 files changed, 199 insertions, 4 deletions
diff --git a/data/templates/accel-ppp/l2tp.config.j2 b/data/templates/accel-ppp/l2tp.config.j2
index 5914fd375..a2f9c9fc7 100644
--- a/data/templates/accel-ppp/l2tp.config.j2
+++ b/data/templates/accel-ppp/l2tp.config.j2
@@ -91,6 +91,9 @@ server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_por
{% if radius_dynamic_author.server is vyos_defined %}
dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
{% endif %}
+{% if radius_acct_interim_interval is vyos_defined %}
+acct-interim-interval={{ radius_acct_interim_interval }}
+{% endif %}
{% if radius_acct_inter_jitter %}
acct-interim-jitter={{ radius_acct_inter_jitter }}
{% endif %}
diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2
index 78a629d2d..0082e55bf 100644
--- a/data/templates/accel-ppp/pptp.config.j2
+++ b/data/templates/accel-ppp/pptp.config.j2
@@ -70,6 +70,9 @@ verbose=1
server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
{% endfor %}
+{% if radius_acct_interim_interval is vyos_defined %}
+acct-interim-interval={{ radius_acct_interim_interval }}
+{% endif %}
{% if radius_acct_inter_jitter %}
acct-interim-jitter={{ radius_acct_inter_jitter }}
{% endif %}
diff --git a/data/templates/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2
index 6ea5f91d0..85b89c70c 100644
--- a/data/templates/high-availability/keepalived.conf.j2
+++ b/data/templates/high-availability/keepalived.conf.j2
@@ -32,9 +32,13 @@ global_defs {
{% if vrrp.group is vyos_defined %}
{% for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %}
-{% if group_config.health_check.script is vyos_defined %}
+{% if group_config.health_check is vyos_defined %}
vrrp_script healthcheck_{{ name }} {
+{% if group_config.health_check.script is vyos_defined %}
script "{{ group_config.health_check.script }}"
+{% elif group_config.health_check.ping is vyos_defined %}
+ script "/usr/bin/ping -c1 {{ group_config.health_check.ping }}"
+{% endif %}
interval {{ group_config.health_check.interval }}
fall {{ group_config.health_check.failure_count }}
rise 1
@@ -121,7 +125,7 @@ vrrp_instance {{ name }} {
{% endfor %}
}
{% endif %}
-{% if group_config.health_check.script is vyos_defined %}
+{% if group_config.health_check is vyos_defined %}
track_script {
healthcheck_{{ name }}
}
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index 9b52fe82e..94253def3 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -96,7 +96,7 @@
#include <include/generic-disable-node.xml.i>
<node name="health-check">
<properties>
- <help>Health check script</help>
+ <help>Health check</help>
</properties>
<children>
<leafNode name="failure-count">
@@ -117,6 +117,23 @@
</properties>
<defaultValue>60</defaultValue>
</leafNode>
+ <leafNode name="ping">
+ <properties>
+ <help>ICMP ping health check</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 ping target address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 ping target address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="script">
<properties>
<help>Health check script file</help>
diff --git a/interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i b/interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i
new file mode 100644
index 000000000..311ef969c
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from accel-ppp/radius-accounting-interim-interval.xml.i -->
+<leafNode name="accounting-interim-interval">
+ <properties>
+ <help>Interval in seconds to send accounting information</help>
+ <valueHelp>
+ <format>u32:1-3600</format>
+ <description>Interval in seconds to send accounting information</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-3600"/>
+ </constraint>
+ <constraintErrorMessage>Interval value must be between 1 and 3600 seconds</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in
index 6b64c5f5d..ec186cd23 100644
--- a/interface-definitions/vpn-l2tp.xml.in
+++ b/interface-definitions/vpn-l2tp.xml.in
@@ -177,6 +177,7 @@
#include <include/radius-auth-server-ipv4.xml.i>
<node name="radius">
<children>
+ #include <include/accel-ppp/radius-accounting-interim-interval.xml.i>
<tagNode name="server">
<children>
#include <include/accel-ppp/radius-additions-disable-accounting.xml.i>
diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py
index 66b40d92b..4afc9f54e 100644
--- a/python/vyos/utils/dict.py
+++ b/python/vyos/utils/dict.py
@@ -233,3 +233,24 @@ def dict_to_list(d, save_key_to=None):
collect.append(item)
return collect
+
+def check_mutually_exclusive_options(d, keys, required=False):
+ """ Checks if a dict has at most one or only one of
+ mutually exclusive keys.
+ """
+ present_keys = []
+
+ for k in d:
+ if k in keys:
+ present_keys.append(k)
+
+ # Un-mangle the keys to make them match CLI option syntax
+ from re import sub
+ orig_keys = list(map(lambda s: sub(r'_', '-', s), keys))
+ orig_present_keys = list(map(lambda s: sub(r'_', '-', s), present_keys))
+
+ if len(present_keys) > 1:
+ raise ValueError(f"Options {orig_keys} are mutually-exclusive but more than one of them is present: {orig_present_keys}")
+
+ if required and (len(present_keys) < 1):
+ raise ValueError(f"At least one of the following options is required: {orig_present_keys}")
diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py
new file mode 100644
index 000000000..843494855
--- /dev/null
+++ b/python/vyos/utils/io.py
@@ -0,0 +1,103 @@
+# Copyright 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
+# 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/>.
+
+def print_error(str='', end='\n'):
+ """
+ Print `str` to stderr, terminated with `end`.
+ Used for warnings and out-of-band messages to avoid mangling precious
+ stdout output.
+ """
+ import sys
+ sys.stderr.write(str)
+ sys.stderr.write(end)
+ sys.stderr.flush()
+
+def make_progressbar():
+ """
+ Make a procedure that takes two arguments `done` and `total` and prints a
+ progressbar based on the ratio thereof, whose length is determined by the
+ width of the terminal.
+ """
+ import shutil, math
+ col, _ = shutil.get_terminal_size()
+ col = max(col - 15, 20)
+ def print_progressbar(done, total):
+ if done <= total:
+ increment = total / col
+ length = math.ceil(done / increment)
+ percentage = str(math.ceil(100 * done / total)).rjust(3)
+ print_error(f'[{length * "#"}{(col - length) * "_"}] {percentage}%', '\r')
+ # Print a newline so that the subsequent prints don't overwrite the full bar.
+ if done == total:
+ print_error()
+ return print_progressbar
+
+def make_incremental_progressbar(increment: float):
+ """
+ Make a generator that displays a progressbar that grows monotonically with
+ every iteration.
+ First call displays it at 0% and every subsequent iteration displays it
+ at `increment` increments where 0.0 < `increment` < 1.0.
+ Intended for FTP and HTTP transfers with stateless callbacks.
+ """
+ print_progressbar = make_progressbar()
+ total = 0.0
+ while total < 1.0:
+ print_progressbar(total, 1.0)
+ yield
+ total += increment
+ print_progressbar(1, 1)
+ # Ignore further calls.
+ while True:
+ yield
+
+def ask_input(question, default='', numeric_only=False, valid_responses=[]):
+ question_out = question
+ if default:
+ question_out += f' (Default: {default})'
+ response = ''
+ while True:
+ response = input(question_out + ' ').strip()
+ if not response and default:
+ return default
+ if numeric_only:
+ if not response.isnumeric():
+ print("Invalid value, try again.")
+ continue
+ response = int(response)
+ if valid_responses and response not in valid_responses:
+ print("Invalid value, try again.")
+ continue
+ break
+ return response
+
+def ask_yes_no(question, default=False) -> bool:
+ """Ask a yes/no question via input() and return their answer."""
+ from sys import stdout
+ default_msg = "[Y/n]" if default else "[y/N]"
+ while True:
+ try:
+ stdout.write("%s %s " % (question, default_msg))
+ c = input().lower()
+ if c == '':
+ return default
+ elif c in ("y", "ye", "yes"):
+ return True
+ elif c in ("n", "no"):
+ return False
+ else:
+ stdout.write("Please respond with yes/y or no/n\n")
+ except EOFError:
+ stdout.write("\nPlease respond with yes/y or no/n\n")
diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py
index 94be0483a..fe2a1c48a 100755
--- a/smoketest/scripts/system/test_kernel_options.py
+++ b/smoketest/scripts/system/test_kernel_options.py
@@ -63,6 +63,19 @@ class TestKernelModules(unittest.TestCase):
self.assertIn(option, config_data,
f"Option {option} is not present in /proc/config.gz")
+ def test_synproxy_enabled(self):
+ options_to_check = [
+ 'CONFIG_NFT_SYNPROXY',
+ 'CONFIG_IP_NF_TARGET_SYNPROXY'
+ ]
+ if not os.path.isfile(CONFIG):
+ call('sudo modprobe configs')
+ with gzip.open(CONFIG, 'rt') as f:
+ config_data = f.read()
+ for option in options_to_check:
+ tmp = re.findall(f'{option}=(y|m)', config_data)
+ self.assertTrue(tmp)
+
def test_qemu_support(self):
# The bond/lacp interface must be enabled in the OS Kernel
for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO',
diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py
index 5e76dd9f9..7a63f5b4b 100755
--- a/src/conf_mode/high-availability.py
+++ b/src/conf_mode/high-availability.py
@@ -106,6 +106,13 @@ def verify(ha):
if not {'password', 'type'} <= set(group_config['authentication']):
raise ConfigError(f'Authentication requires both type and passwortd to be set in VRRP group "{group}"')
+ if 'health_check' in group_config:
+ from vyos.utils.dict import check_mutually_exclusive_options
+ try:
+ check_mutually_exclusive_options(group_config["health_check"], ["script", "ping"], required=True)
+ except ValueError as e:
+ raise ConfigError(f'Health check config is incorrect in VRRP group "{group}": {e}')
+
# Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction
# We also need to make sure VRID is not used twice on the same interface with the
# same address family.
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index 65623c2b1..ffac3b023 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -63,6 +63,7 @@ default_config_data = {
'ppp_ipv6_peer_intf_id': None,
'radius_server': [],
'radius_acct_inter_jitter': '',
+ 'radius_acct_interim_interval': None,
'radius_acct_tmo': '3',
'radius_max_try': '3',
'radius_timeout': '3',
@@ -190,6 +191,9 @@ def get_config(config=None):
# advanced radius-setting
conf.set_level(base_path + ['authentication', 'radius'])
+ if conf.exists(['accounting-interim-interval']):
+ l2tp['radius_acct_interim_interval'] = conf.return_value(['accounting-interim-interval'])
+
if conf.exists(['acct-interim-jitter']):
l2tp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter'])
diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py
index 986a19972..b9d18110a 100755
--- a/src/conf_mode/vpn_pptp.py
+++ b/src/conf_mode/vpn_pptp.py
@@ -37,6 +37,7 @@ default_pptp = {
'local_users' : [],
'radius_server' : [],
'radius_acct_inter_jitter': '',
+ 'radius_acct_interim_interval': None,
'radius_acct_tmo' : '30',
'radius_max_try' : '3',
'radius_timeout' : '30',
@@ -145,6 +146,9 @@ def get_config(config=None):
# advanced radius-setting
conf.set_level(base_path + ['authentication', 'radius'])
+ if conf.exists(['accounting-interim-interval']):
+ pptp['radius_acct_interim_interval'] = conf.return_value(['accounting-interim-interval'])
+
if conf.exists(['acct-interim-jitter']):
pptp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter'])