Files
neutron/neutron/agent/l3/namespaces.py
T
Slawek Kaplonski 3e9e2a5b4b Disable IPv6 forwarding by default on HA routers
In case of HA routers IPv6 forwarding is not disabled by default and
then enabled only on master node.
Before this patch it was done in opposite way, so forwarding was
enabled by default and then disabled on backup nodes.
When forwarding was enabled/disabled for qg- port, MLDv2 packets are
sent and that might lead to temportary packets loss as packets to
FIP were sent to this backup node instead of master one.

Related-Bug: #1771841

Change-Id: Ia6b772e91c1f94612ca29d7082eca999372e60d6
2018-05-31 20:19:21 +00:00

151 lines
5.1 KiB
Python

# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import functools
from oslo_log import log as logging
from oslo_utils import excutils
from neutron.agent.linux import ip_lib
LOG = logging.getLogger(__name__)
NS_PREFIX = 'qrouter-'
INTERNAL_DEV_PREFIX = 'qr-'
EXTERNAL_DEV_PREFIX = 'qg-'
# TODO(Carl) It is odd that this file needs this. It is a dvr detail.
ROUTER_2_FIP_DEV_PREFIX = 'rfp-'
def build_ns_name(prefix, identifier):
"""Builds a namespace name from the given prefix and identifier
:param prefix: The prefix which must end with '-' for legacy reasons
:param identifier: The id associated with the namespace
"""
return prefix + identifier
def get_prefix_from_ns_name(ns_name):
"""Parses prefix from prefix-identifier
:param ns_name: The name of a namespace
:returns: The prefix ending with a '-' or None if there is no '-'
"""
dash_index = ns_name.find('-')
if 0 <= dash_index:
return ns_name[:dash_index + 1]
def get_id_from_ns_name(ns_name):
"""Parses identifier from prefix-identifier
:param ns_name: The name of a namespace
:returns: Identifier or None if there is no - to end the prefix
"""
dash_index = ns_name.find('-')
if 0 <= dash_index:
return ns_name[dash_index + 1:]
def check_ns_existence(f):
@functools.wraps(f)
def wrapped(self, *args, **kwargs):
if not self.exists():
LOG.warning('Namespace %(name)s does not exist. Skipping '
'%(func)s',
{'name': self.name, 'func': f.__name__})
return
try:
return f(self, *args, **kwargs)
except RuntimeError:
with excutils.save_and_reraise_exception() as ctx:
if not self.exists():
LOG.debug('Namespace %(name)s was concurrently deleted',
self.name)
ctx.reraise = False
return wrapped
class Namespace(object):
def __init__(self, name, agent_conf, driver, use_ipv6):
self.name = name
self.ip_wrapper_root = ip_lib.IPWrapper()
self.agent_conf = agent_conf
self.driver = driver
self.use_ipv6 = use_ipv6
def create(self, ipv6_forwarding=True):
# See networking (netdev) tree, file
# Documentation/networking/ip-sysctl.txt for an explanation of
# these sysctl values.
ip_wrapper = self.ip_wrapper_root.ensure_namespace(self.name)
cmd = ['sysctl', '-w', 'net.ipv4.ip_forward=1']
ip_wrapper.netns.execute(cmd)
# 1. Reply only if the target IP address is local address configured
# on the incoming interface; and
# 2. Always use the best local address
cmd = ['sysctl', '-w', 'net.ipv4.conf.all.arp_ignore=1']
ip_wrapper.netns.execute(cmd)
cmd = ['sysctl', '-w', 'net.ipv4.conf.all.arp_announce=2']
ip_wrapper.netns.execute(cmd)
if self.use_ipv6:
cmd = ['sysctl', '-w',
'net.ipv6.conf.all.forwarding=%d' % int(ipv6_forwarding)]
ip_wrapper.netns.execute(cmd)
def delete(self):
try:
self.ip_wrapper_root.netns.delete(self.name)
except RuntimeError:
msg = 'Failed trying to delete namespace: %s'
LOG.exception(msg, self.name)
def exists(self):
return self.ip_wrapper_root.netns.exists(self.name)
class RouterNamespace(Namespace):
def __init__(self, router_id, agent_conf, driver, use_ipv6):
self.router_id = router_id
name = self._get_ns_name(router_id)
super(RouterNamespace, self).__init__(
name, agent_conf, driver, use_ipv6)
@classmethod
def _get_ns_name(cls, router_id):
return build_ns_name(NS_PREFIX, router_id)
@check_ns_existence
def delete(self):
ns_ip = ip_lib.IPWrapper(namespace=self.name)
for d in ns_ip.get_devices():
if d.name.startswith(INTERNAL_DEV_PREFIX):
# device is on default bridge
self.driver.unplug(d.name, namespace=self.name,
prefix=INTERNAL_DEV_PREFIX)
elif d.name.startswith(ROUTER_2_FIP_DEV_PREFIX):
ns_ip.del_veth(d.name)
elif d.name.startswith(EXTERNAL_DEV_PREFIX):
self.driver.unplug(
d.name,
bridge=self.agent_conf.external_network_bridge,
namespace=self.name,
prefix=EXTERNAL_DEV_PREFIX)
super(RouterNamespace, self).delete()