diff options
-rw-r--r-- | data/templates/router-advert/radvd.conf.j2 | 3 | ||||
-rw-r--r-- | interface-definitions/service-router-advert.xml.in | 17 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_router-advert.py | 14 | ||||
-rwxr-xr-x | src/conf_mode/service_router-advert.py | 34 |
4 files changed, 58 insertions, 10 deletions
diff --git a/data/templates/router-advert/radvd.conf.j2 b/data/templates/router-advert/radvd.conf.j2 index 6902dc05a..ed15b32f0 100644 --- a/data/templates/router-advert/radvd.conf.j2 +++ b/data/templates/router-advert/radvd.conf.j2 @@ -55,6 +55,9 @@ interface {{ iface }} { {% endif %} {% if iface_config.name_server is vyos_defined %} RDNSS {{ iface_config.name_server | join(" ") }} { +{% if iface_config.name_server_lifetime is vyos_defined %} + AdvRDNSSLifetime {{ iface_config.name_server_lifetime }}; +{% endif %} }; {% endif %} {% if iface_config.dnssl is vyos_defined %} diff --git a/interface-definitions/service-router-advert.xml.in b/interface-definitions/service-router-advert.xml.in index 40dac23ca..258b7b749 100644 --- a/interface-definitions/service-router-advert.xml.in +++ b/interface-definitions/service-router-advert.xml.in @@ -136,6 +136,23 @@ </children> </node> #include <include/name-server-ipv6.xml.i> + <leafNode name="name-server-lifetime"> + <properties> + <help>Maximum duration how long the RDNSS entries are used</help> + <valueHelp> + <format>u32:0</format> + <description>Name-servers should no longer be used</description> + </valueHelp> + <valueHelp> + <format>u32:1-7200</format> + <description>Maximum interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-7200"/> + </constraint> + <constraintErrorMessage>Maximum interval must be between 1 and 7200 seconds</constraintErrorMessage> + </properties> + </leafNode> <leafNode name="other-config-flag"> <properties> <help>Hosts use the administered (stateful) protocol for autoconfiguration of other (non-address) information</help> diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index 4875fb5d1..1168c05cd 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -17,6 +17,7 @@ import re import unittest +from vyos.configsession import ConfigSessionError from base_vyostest_shim import VyOSUnitTestSHIM from vyos.util import read_file @@ -93,6 +94,7 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): def test_dns(self): nameserver = ['2001:db8::1', '2001:db8::2'] dnssl = ['vyos.net', 'vyos.io'] + ns_lifetime = '599' self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity']) self.cli_set(base_path + ['other-config-flag']) @@ -102,6 +104,14 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): for sl in dnssl: self.cli_set(base_path + ['dnssl', sl]) + self.cli_set(base_path + ['name-server-lifetime', ns_lifetime]) + # The value, if not 0, must be at least interval max (defaults to 600). + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + ns_lifetime = '600' + self.cli_set(base_path + ['name-server-lifetime', ns_lifetime]) + # commit changes self.cli_commit() @@ -110,8 +120,12 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): tmp = 'RDNSS ' + ' '.join(nameserver) + ' {' self.assertIn(tmp, config) + tmp = f'AdvRDNSSLifetime {ns_lifetime};' + self.assertIn(tmp, config) + tmp = 'DNSSL ' + ' '.join(dnssl) + ' {' self.assertIn(tmp, config) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py index 71b758399..ff7caaa84 100755 --- a/src/conf_mode/service_router-advert.py +++ b/src/conf_mode/service_router-advert.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2021 VyOS maintainers and contributors +# Copyright (C) 2018-2022 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 @@ -17,7 +17,7 @@ import os from sys import exit - +from vyos.base import Warning from vyos.config import Config from vyos.configdict import dict_merge from vyos.template import render @@ -79,22 +79,35 @@ def verify(rtradv): if 'interface' not in rtradv: return None - for interface in rtradv['interface']: - interface = rtradv['interface'][interface] + for interface, interface_config in rtradv['interface'].items(): if 'prefix' in interface: - for prefix in interface['prefix']: - prefix = interface['prefix'][prefix] - valid_lifetime = prefix['valid_lifetime'] + for prefix, prefix_config in interface_config['prefix'].items(): + valid_lifetime = prefix_config['valid_lifetime'] if valid_lifetime == 'infinity': valid_lifetime = 4294967295 - preferred_lifetime = prefix['preferred_lifetime'] + preferred_lifetime = prefix_config['preferred_lifetime'] if preferred_lifetime == 'infinity': preferred_lifetime = 4294967295 if not (int(valid_lifetime) > int(preferred_lifetime)): raise ConfigError('Prefix valid-lifetime must be greater then preferred-lifetime') + if 'name_server_lifetime' in interface_config: + # man page states: + # The maximum duration how long the RDNSS entries are used for name + # resolution. A value of 0 means the nameserver must no longer be + # used. The value, if not 0, must be at least MaxRtrAdvInterval. To + # ensure stale RDNSS info gets removed in a timely fashion, this + # should not be greater than 2*MaxRtrAdvInterval. + lifetime = int(interface_config['name_server_lifetime']) + interval_max = int(interface_config['interval']['max']) + if lifetime > 0: + if lifetime < int(interval_max): + raise ConfigError(f'RDNSS lifetime must be at least "{interval_max}" seconds!') + if lifetime > 2* interval_max: + Warning(f'RDNSS lifetime should not exceed "{2 * interval_max}" which is two times "interval max"!') + return None def generate(rtradv): @@ -105,15 +118,16 @@ def generate(rtradv): return None def apply(rtradv): + systemd_service = 'radvd.service' if not rtradv: # bail out early - looks like removal from running config - call('systemctl stop radvd.service') + call(f'systemctl stop {systemd_service}') if os.path.exists(config_file): os.unlink(config_file) return None - call('systemctl restart radvd.service') + call(f'systemctl reload-or-restart {systemd_service}') return None |