Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F119064016
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
View Options
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
@@ -1,66 +1,69 @@
### Autogenerated by service_router-advert.py ###
{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
interface {{ iface }} {
IgnoreIfMissing on;
{% if iface_config.default_preference is vyos_defined %}
AdvDefaultPreference {{ iface_config.default_preference }};
{% endif %}
{% if iface_config.managed_flag is vyos_defined %}
AdvManagedFlag {{ 'on' if iface_config.managed_flag is vyos_defined else 'off' }};
{% endif %}
{% if iface_config.interval.max is vyos_defined %}
MaxRtrAdvInterval {{ iface_config.interval.max }};
{% endif %}
{% if iface_config.interval.min is vyos_defined %}
MinRtrAdvInterval {{ iface_config.interval.min }};
{% endif %}
{% if iface_config.reachable_time is vyos_defined %}
AdvReachableTime {{ iface_config.reachable_time }};
{% endif %}
AdvIntervalOpt {{ 'off' if iface_config.no_send_advert is vyos_defined else 'on' }};
AdvSendAdvert {{ 'off' if iface_config.no_send_advert is vyos_defined else 'on' }};
{% if iface_config.default_lifetime is vyos_defined %}
AdvDefaultLifetime {{ iface_config.default_lifetime }};
{% endif %}
{% if iface_config.link_mtu is vyos_defined %}
AdvLinkMTU {{ iface_config.link_mtu }};
{% endif %}
AdvOtherConfigFlag {{ 'on' if iface_config.other_config_flag is vyos_defined else 'off' }};
AdvRetransTimer {{ iface_config.retrans_timer }};
AdvCurHopLimit {{ iface_config.hop_limit }};
{% if iface_config.route is vyos_defined %}
{% for route, route_options in iface_config.route.items() %}
route {{ route }} {
{% if route_options.valid_lifetime is vyos_defined %}
AdvRouteLifetime {{ route_options.valid_lifetime }};
{% endif %}
{% if route_options.route_preference is vyos_defined %}
AdvRoutePreference {{ route_options.route_preference }};
{% endif %}
RemoveRoute {{ 'off' if route_options.no_remove_route is vyos_defined else 'on' }};
};
{% endfor %}
{% endif %}
{% if iface_config.prefix is vyos_defined %}
{% for prefix, prefix_options in iface_config.prefix.items() %}
prefix {{ prefix }} {
AdvAutonomous {{ 'off' if prefix_options.no_autonomous_flag is vyos_defined else 'on' }};
AdvValidLifetime {{ prefix_options.valid_lifetime }};
AdvOnLink {{ 'off' if prefix_options.no_on_link_flag is vyos_defined else 'on' }};
AdvPreferredLifetime {{ prefix_options.preferred_lifetime }};
};
{% endfor %}
{% 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 %}
DNSSL {{ iface_config.dnssl | join(" ") }} {
};
{% endif %}
};
{% endfor %}
{% endif %}
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
@@ -1,327 +1,344 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="service">
<children>
<node name="router-advert" owner="${vyos_conf_scripts_dir}/service_router-advert.py">
<properties>
<help>IPv6 Router Advertisements (RAs) service</help>
<priority>900</priority>
</properties>
<children>
<tagNode name="interface">
<properties>
<help>Interface to send RA on</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
</properties>
<children>
<leafNode name="hop-limit">
<properties>
<help>Set Hop Count field of the IP header for outgoing packets</help>
<valueHelp>
<format>u32:0</format>
<description>Unspecified (by this router)</description>
</valueHelp>
<valueHelp>
<format>u32:1-255</format>
<description>Value should represent current diameter of the Internet</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-255"/>
</constraint>
<constraintErrorMessage>Hop count must be between 0 and 255</constraintErrorMessage>
</properties>
<defaultValue>64</defaultValue>
</leafNode>
<leafNode name="default-lifetime">
<properties>
<help>Lifetime associated with the default router in units of seconds</help>
<valueHelp>
<format>u32:4-9000</format>
<description>Router Lifetime in seconds</description>
</valueHelp>
<valueHelp>
<format>0</format>
<description>Not a default router</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 4-9000"/>
</constraint>
<constraintErrorMessage>Default router livetime bust be 0 or between 4 and 9000</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="default-preference">
<properties>
<help>Preference associated with the default router,</help>
<completionHelp>
<list>low medium high</list>
</completionHelp>
<valueHelp>
<format>low</format>
<description>Default router has low preference</description>
</valueHelp>
<valueHelp>
<format>medium</format>
<description>Default router has medium preference</description>
</valueHelp>
<valueHelp>
<format>high</format>
<description>Default router has high preference</description>
</valueHelp>
<constraint>
<regex>(low|medium|high)</regex>
</constraint>
<constraintErrorMessage>Default preference must be low, medium or high</constraintErrorMessage>
</properties>
<defaultValue>medium</defaultValue>
</leafNode>
<leafNode name="dnssl">
<properties>
<help>DNS search list</help>
<multi/>
</properties>
</leafNode>
<leafNode name="link-mtu">
<properties>
<help>Link MTU value placed in RAs, exluded in RAs if unset</help>
<valueHelp>
<format>u32:1280-9000</format>
<description>Link MTU value in RAs</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1280-9000"/>
</constraint>
<constraintErrorMessage>Link MTU must be between 1280 and 9000</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="managed-flag">
<properties>
<help>Hosts use the administered (stateful) protocol for address autoconfiguration in addition to any addresses autoconfigured using SLAAC</help>
<valueless/>
</properties>
</leafNode>
<node name="interval">
<properties>
<help>Set interval between unsolicited multicast RAs</help>
</properties>
<children>
<leafNode name="max">
<properties>
<help>Maximum interval between unsolicited multicast RAs</help>
<valueHelp>
<format>u32:4-1800</format>
<description>Maximum interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 4-1800"/>
</constraint>
<constraintErrorMessage>Maximum interval must be between 4 and 1800 seconds</constraintErrorMessage>
</properties>
<defaultValue>600</defaultValue>
</leafNode>
<leafNode name="min">
<properties>
<help>Minimum interval between unsolicited multicast RAs</help>
<valueHelp>
<format>u32:3-1350</format>
<description>Minimum interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 3-1350"/>
</constraint>
<constraintErrorMessage>Minimum interval must be between 3 and 1350 seconds</constraintErrorMessage>
</properties>
</leafNode>
</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>
<valueless/>
</properties>
</leafNode>
<tagNode name="route">
<properties>
<help>IPv6 route to be advertised in Router Advertisements (RAs)</help>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 route to be advertized</description>
</valueHelp>
<constraint>
<validator name="ipv6-prefix"/>
</constraint>
</properties>
<children>
<leafNode name="valid-lifetime">
<properties>
<help>Time in seconds that the route will remain valid</help>
<completionHelp>
<list>infinity</list>
</completionHelp>
<valueHelp>
<format>u32:1-4294967295</format>
<description>Time in seconds that the route will remain valid</description>
</valueHelp>
<valueHelp>
<format>infinity</format>
<description>Route will remain preferred forever</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4294967295"/>
<regex>(infinity)</regex>
</constraint>
</properties>
<defaultValue>1800</defaultValue>
</leafNode>
<leafNode name="route-preference">
<properties>
<help>Preference associated with the route,</help>
<completionHelp>
<list>low medium high</list>
</completionHelp>
<valueHelp>
<format>low</format>
<description>Route has low preference</description>
</valueHelp>
<valueHelp>
<format>medium</format>
<description>Route has medium preference</description>
</valueHelp>
<valueHelp>
<format>high</format>
<description>Route has high preference</description>
</valueHelp>
<constraint>
<regex>(low|medium|high)</regex>
</constraint>
<constraintErrorMessage>Route preference must be low, medium or high</constraintErrorMessage>
</properties>
<defaultValue>medium</defaultValue>
</leafNode>
<leafNode name="no-remove-route">
<properties>
<help>Do not announce this route with a zero second lifetime upon shutdown</help>
<valueless/>
</properties>
</leafNode>
</children>
</tagNode>
<tagNode name="prefix">
<properties>
<help>IPv6 prefix to be advertised in Router Advertisements (RAs)</help>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 prefix to be advertized</description>
</valueHelp>
<constraint>
<validator name="ipv6-prefix"/>
</constraint>
</properties>
<children>
<leafNode name="no-autonomous-flag">
<properties>
<help>Prefix can not be used for stateless address auto-configuration</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="no-on-link-flag">
<properties>
<help>Prefix can not be used for on-link determination</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="preferred-lifetime">
<properties>
<help>Time in seconds that the prefix will remain preferred</help>
<completionHelp>
<list>infinity</list>
</completionHelp>
<valueHelp>
<format>u32</format>
<description>Time in seconds that the prefix will remain preferred</description>
</valueHelp>
<valueHelp>
<format>infinity</format>
<description>Prefix will remain preferred forever</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4294967295"/>
<regex>(infinity)</regex>
</constraint>
</properties>
<defaultValue>14400</defaultValue>
</leafNode>
<leafNode name="valid-lifetime">
<properties>
<help>Time in seconds that the prefix will remain valid</help>
<completionHelp>
<list>infinity</list>
</completionHelp>
<valueHelp>
<format>u32:1-4294967295</format>
<description>Time in seconds that the prefix will remain valid</description>
</valueHelp>
<valueHelp>
<format>infinity</format>
<description>Prefix will remain preferred forever</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4294967295"/>
<regex>(infinity)</regex>
</constraint>
</properties>
<defaultValue>2592000</defaultValue>
</leafNode>
</children>
</tagNode>
<leafNode name="reachable-time">
<properties>
<help>Time, in milliseconds, that a node assumes a neighbor is reachable after having received a reachability confirmation</help>
<valueHelp>
<format>u32:0</format>
<description>Reachable Time unspecified by this router</description>
</valueHelp>
<valueHelp>
<format>u32:1-3600000</format>
<description>Reachable Time value in RAs (in milliseconds)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 1-3600000"/>
</constraint>
<constraintErrorMessage>Reachable time must be 0 or between 1 and 3600000 milliseconds</constraintErrorMessage>
</properties>
<defaultValue>0</defaultValue>
</leafNode>
<leafNode name="retrans-timer">
<properties>
<help>Time in milliseconds between retransmitted Neighbor Solicitation messages</help>
<valueHelp>
<format>u32:0</format>
<description>Time, in milliseconds, between retransmitted Neighbor Solicitation messages</description>
</valueHelp>
<valueHelp>
<format>u32:1-4294967295</format>
<description>Minimum interval in milliseconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 1-4294967295"/>
</constraint>
<constraintErrorMessage>Retransmit interval must be 0 or between 1 and 4294967295 milliseconds</constraintErrorMessage>
</properties>
<defaultValue>0</defaultValue>
</leafNode>
<leafNode name="no-send-advert">
<properties>
<help>Do not send router adverts</help>
<valueless/>
</properties>
</leafNode>
</children>
</tagNode>
</children>
</node>
</children>
</node>
</interfaceDefinition>
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
@@ -1,117 +1,131 @@
#!/usr/bin/env python3
#
# 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
# 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 re
import unittest
+from vyos.configsession import ConfigSessionError
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.util import read_file
from vyos.util import process_named_running
RADVD_CONF = '/run/radvd/radvd.conf'
interface = 'eth1'
base_path = ['service', 'router-advert', 'interface', interface]
address_base = ['interfaces', 'ethernet', interface, 'address']
def get_config_value(key):
tmp = read_file(RADVD_CONF)
tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
return tmp[0].split()[0].replace(';','')
class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
def setUp(self):
self.cli_set(address_base + ['2001:db8::1/64'])
def tearDown(self):
self.cli_delete(address_base)
self.cli_delete(base_path)
self.cli_commit()
def test_common(self):
self.cli_set(base_path + ['prefix', '::/64', 'no-on-link-flag'])
self.cli_set(base_path + ['prefix', '::/64', 'no-autonomous-flag'])
self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity'])
self.cli_set(base_path + ['other-config-flag'])
# commit changes
self.cli_commit()
# verify values
tmp = get_config_value('interface')
self.assertEqual(tmp, interface)
tmp = get_config_value('prefix')
self.assertEqual(tmp, '::/64')
tmp = get_config_value('AdvOtherConfigFlag')
self.assertEqual(tmp, 'on')
# this is a default value
tmp = get_config_value('AdvRetransTimer')
self.assertEqual(tmp, '0')
# this is a default value
tmp = get_config_value('AdvCurHopLimit')
self.assertEqual(tmp, '64')
# this is a default value
tmp = get_config_value('AdvDefaultPreference')
self.assertEqual(tmp, 'medium')
tmp = get_config_value('AdvAutonomous')
self.assertEqual(tmp, 'off')
# this is a default value
tmp = get_config_value('AdvValidLifetime')
self.assertEqual(tmp, 'infinity')
# this is a default value
tmp = get_config_value('AdvPreferredLifetime')
self.assertEqual(tmp, '14400')
tmp = get_config_value('AdvOnLink')
self.assertEqual(tmp, 'off')
# Check for running process
self.assertTrue(process_named_running('radvd'))
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'])
for ns in nameserver:
self.cli_set(base_path + ['name-server', ns])
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()
config = read_file(RADVD_CONF)
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,128 +1,142 @@
#!/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
# 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
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
from vyos.util import call
from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
config_file = r'/run/radvd/radvd.conf'
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
base = ['service', 'router-advert']
rtradv = 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_interface_values = defaults(base + ['interface'])
# we deal with prefix, route defaults later on
if 'prefix' in default_interface_values:
del default_interface_values['prefix']
if 'route' in default_interface_values:
del default_interface_values['route']
default_prefix_values = defaults(base + ['interface', 'prefix'])
default_route_values = defaults(base + ['interface', 'route'])
if 'interface' in rtradv:
for interface in rtradv['interface']:
rtradv['interface'][interface] = dict_merge(
default_interface_values, rtradv['interface'][interface])
if 'prefix' in rtradv['interface'][interface]:
for prefix in rtradv['interface'][interface]['prefix']:
rtradv['interface'][interface]['prefix'][prefix] = dict_merge(
default_prefix_values, rtradv['interface'][interface]['prefix'][prefix])
if 'route' in rtradv['interface'][interface]:
for route in rtradv['interface'][interface]['route']:
rtradv['interface'][interface]['route'][route] = dict_merge(
default_route_values, rtradv['interface'][interface]['route'][route])
if 'name_server' in rtradv['interface'][interface]:
# always use a list when dealing with nameservers - eases the template generation
if isinstance(rtradv['interface'][interface]['name_server'], str):
rtradv['interface'][interface]['name_server'] = [
rtradv['interface'][interface]['name_server']]
return rtradv
def verify(rtradv):
if not rtradv:
return None
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):
if not rtradv:
return None
render(config_file, 'router-advert/radvd.conf.j2', rtradv, permission=0o644)
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
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 6, 9:23 AM (18 h, 36 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4653875
Default Alt Text
(30 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment