1 # Copyright 2014 OpenStack Foundation
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
16 from itertools import chain as iter_chain
19 from oslo_config import cfg
20 from oslo_log import log as logging
23 from neutron._i18n import _
24 from neutron.agent.linux import external_process
25 from neutron.agent.linux import utils
26 from neutron.common import constants
27 from neutron.common import utils as common_utils
30 RADVD_SERVICE_NAME = 'radvd'
31 RADVD_SERVICE_CMD = 'radvd'
32 # We can configure max of 3 DNS servers in radvd RDNSS section.
35 LOG = logging.getLogger(__name__)
38 cfg.StrOpt('ra_confs',
39 default='$state_path/ra',
40 help=_('Location to store IPv6 RA config files')),
43 CONFIG_TEMPLATE = jinja2.Template("""interface {{ interface_name }}
49 {% if constants.DHCPV6_STATELESS in ra_modes %}
50 AdvOtherConfigFlag on;
53 {% if constants.DHCPV6_STATEFUL in ra_modes %}
58 RDNSS {% for dns in dns_servers %} {{ dns }} {% endfor %} {};
61 {% for prefix in prefixes %}
72 class DaemonMonitor(object):
73 """Manage the data and state of an radvd process."""
75 def __init__(self, router_id, router_ns, process_monitor, dev_name_helper,
77 self._router_id = router_id
78 self._router_ns = router_ns
79 self._process_monitor = process_monitor
80 self._dev_name_helper = dev_name_helper
81 self._agent_conf = agent_conf
83 def _generate_radvd_conf(self, router_ports):
84 radvd_conf = utils.get_conf_file_name(self._agent_conf.ra_confs,
89 for p in router_ports:
90 subnets = p.get('subnets', [])
91 v6_subnets = [subnet for subnet in subnets if
92 netaddr.IPNetwork(subnet['cidr']).version == 6]
95 ra_modes = {subnet['ipv6_ra_mode'] for subnet in v6_subnets}
96 auto_config_prefixes = [subnet['cidr'] for subnet in v6_subnets if
97 subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC or
98 subnet['ipv6_ra_mode'] == constants.DHCPV6_STATELESS]
99 interface_name = self._dev_name_helper(p['id'])
100 slaac_subnets = [subnet for subnet in v6_subnets if
101 subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC]
102 dns_servers = list(iter_chain(*[subnet['dns_nameservers'] for
103 subnet in slaac_subnets if subnet.get('dns_nameservers')]))
104 buf.write('%s' % CONFIG_TEMPLATE.render(
105 ra_modes=list(ra_modes),
106 interface_name=interface_name,
107 prefixes=auto_config_prefixes,
108 dns_servers=dns_servers[0:MAX_RDNSS_ENTRIES],
109 constants=constants))
111 common_utils.replace_file(radvd_conf, buf.getvalue())
114 def _get_radvd_process_manager(self, callback=None):
115 return external_process.ProcessManager(
116 uuid=self._router_id,
117 default_cmd_callback=callback,
118 namespace=self._router_ns,
119 service=RADVD_SERVICE_NAME,
120 conf=self._agent_conf,
123 def _spawn_radvd(self, radvd_conf):
124 def callback(pid_file):
125 # we need to use -m syslog and f.e. not -m stderr (the default)
126 # or -m stderr_syslog so that radvd 2.0+ will close stderr and
127 # exit after daemonization; otherwise, the current thread will
128 # be locked waiting for result from radvd that won't ever come
129 # until the process dies
130 radvd_cmd = [RADVD_SERVICE_CMD,
131 '-C', '%s' % radvd_conf,
132 '-p', '%s' % pid_file,
136 pm = self._get_radvd_process_manager(callback)
137 pm.enable(reload_cfg=True)
138 self._process_monitor.register(uuid=self._router_id,
139 service_name=RADVD_SERVICE_NAME,
140 monitored_process=pm)
141 LOG.debug("radvd enabled for router %s", self._router_id)
143 def enable(self, router_ports):
144 for p in router_ports:
145 for subnet in p['subnets']:
146 if netaddr.IPNetwork(subnet['cidr']).version == 6:
147 LOG.debug("Enable IPv6 RA for router %s", self._router_id)
148 radvd_conf = self._generate_radvd_conf(router_ports)
149 self._spawn_radvd(radvd_conf)
152 # Kill the daemon if it's running
156 self._process_monitor.unregister(uuid=self._router_id,
157 service_name=RADVD_SERVICE_NAME)
158 pm = self._get_radvd_process_manager()
160 utils.remove_conf_files(self._agent_conf.ra_confs, self._router_id)
161 LOG.debug("radvd disabled for router %s", self._router_id)
165 return self._get_radvd_process_manager().active