Page MenuHomeVyOS Platform

No OneTemporary

Size
30 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Fri, Nov 6, 9:23 AM (16 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4653875
Default Alt Text
(30 KB)

Event Timeline