Skip to content

Instantly share code, notes, and snippets.

@Xeite
Created December 4, 2018 05:28
Show Gist options
  • Save Xeite/717f5823e3577f9ee612e63543a64e8d to your computer and use it in GitHub Desktop.
Save Xeite/717f5823e3577f9ee612e63543a64e8d to your computer and use it in GitHub Desktop.
# Copyright 2018 Kakao Corp.
# All Rights Reserved.
#
# 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 sys
from oslo_config import cfg
from oslo_log import log as logging
from neutron_lib import constants
from neutron.plugins.ml2.drivers.linuxbridge.agent.linuxbridge_neutron_agent \
import LinuxBridgeManager
from neutron.plugins.ml2.drivers.linuxbridge.agent.common \
import constants as linux_bridge_constants
from neutron.agent.common import utils
from neutron.agent.l2 import l2_agent_extension
from neutron.agent.linux import ip_lib
from networking_kakao.agent.common import vrf
opts = [
cfg.StrOpt('hostroute_phy_interface', default=None,
help=("Physical interface for external network traffic attached "
"to VRF device. All outgoing traffic goes through this "
"interface. User must to specify this interface for"
"outgoing traffic."))
]
cfg.CONF.register_opts(opts, "FIB")
LOG = logging.getLogger(__name__)
class FibPopulationAgentExtension(l2_agent_extension.L2AgentExtension):
PERMITTED_DEVICE_OWNERS = {constants.DEVICE_OWNER_COMPUTE_PREFIX}
def initialize(self, connection, driver_type):
"""Perform FIB Agent Extension initialization."""
valid_driver_types = (linux_bridge_constants.EXTENSION_DRIVER_TYPE,)
if driver_type not in valid_driver_types:
LOG.error('FIB extension is only supported for linux bridge agent, '
'currently uses %(driver_type)s',
{'driver_type': driver_type})
sys.exit(1)
interface = cfg.CONF.FIB.hostroute_phy_interface
device = ip_lib.IPDevice(interface)
if not device.exists():
LOG.error('Invalid configuration provided for FIB extension: '
'no physical devices')
sys.exit(1)
LOG.info('Interface for hostroute: %s', interface)
self._devices = {}
self._unknown_devices = LinuxBridgeManager.get_all_devices()
self._stale_cleaned = False
LOG.debug('Devices %s will be identified', self._unknown_devices)
self._create_vrf(vrf.INTERFACE)
self._enslave_interface(vrf.INTERFACE, interface)
# NOTE(Yan): decrease the priority of the local table to ensure a
# consistent data path for the VRF device. If not, packets from
# hypervisor return directly following local table.
vrf.set_rule(vrf.LOCAL_RT_PRIORITY, vrf.LOCAL_RT_TABLE)
def handle_port(self, context, details):
"""Handle agent FIB population extension for port."""
device = details['device']
device_owner = details['device_owner']
if device in self._unknown_devices:
self._unknown_devices.remove(device)
LOG.debug('Device %s is identified', device)
if self._is_valid_device_owner(device_owner):
self._create_vrf(vrf.INTERFACE)
network_id = details['network_id']
port_id = details['port_id']
bridge_name = LinuxBridgeManager.get_bridge_name(network_id)
self._enslave_interface(vrf.INTERFACE, bridge_name)
self._enable_proxy_arp(bridge_name)
fixed_ips = []
bridge_device = ip_lib.IPDevice(bridge_name)
for fixed_ip in details['fixed_ips']:
ip_address = fixed_ip['ip_address']
bridge_device.route.add_route(ip_address,
table=vrf.RT_TABLE,
proto=vrf.RT_PROTO)
LOG.info("FIB route entry in %s added: %s", bridge_name,
ip_address)
fixed_ips.append(ip_address)
self._devices[port_id] = {
'device': device,
'bridge_name': bridge_name,
'fixed_ips': fixed_ips
}
if not self._unknown_devices and not self._stale_cleaned:
LOG.info('Start to clean stale devices')
self._delete_stale_route()
def delete_port(self, context, details):
port_id = details['port_id']
if port_id not in self._devices:
LOG.warning('Port %s not found in agent extension')
return
port = self._devices.pop(port_id)
bridge_name = port['bridge_name']
fixed_ips = port['fixed_ips']
bridge_device = ip_lib.IPDevice(bridge_name)
for fixed_ip in fixed_ips:
bridge_device.route.delete_route(fixed_ip,
table=vrf.RT_TABLE,
proto=vrf.RT_PROTO)
LOG.info("FIB route entry in %s removed: %s", bridge_name,
fixed_ip)
@staticmethod
def _create_vrf(vrf_name):
if not vrf.existed(vrf_name):
vrf.create(vrf.INTERFACE, vrf.RT_TABLE)
if not vrf.rules(priority=vrf.RT_PRIORITY):
vrf.add_rule(
vrf.RT_PRIORITY,
vrf.RT_TABLE,
vrf.INTERFACE)
LOG.info('Route rule (%s %s %s) is added', vrf.INTERFACE,
vrf.RT_TABLE, vrf.RT_PRIORITY)
if not vrf.rules(priority=vrf.RT_PRIORITY + 1):
action = 'FR_ACT_UNREACHABLE'
vrf.add_rule(
vrf.RT_PRIORITY + 1,
vrf.RT_TABLE,
vrf.INTERFACE,
action=action)
LOG.info('Route rule (%s %s %s %s) is added', vrf.INTERFACE,
vrf.RT_TABLE, vrf.RT_PRIORITY, action)
LOG.info('VRF device %s using table %s is created', vrf.INTERFACE,
vrf.RT_TABLE)
@staticmethod
def _enslave_interface(vrf_name, interface_name):
if not vrf.having(vrf_name, interface_name):
vrf.add_port(vrf_name, interface_name)
LOG.info('Interface %s is enslaved to VRF %s', interface_name,
vrf_name)
def _delete_stale_route(self):
fixed_ips = sum(
[device['fixed_ips'] for device in self._devices.values()], [])
LOG.info('Fixed IPs identified: %s', fixed_ips)
routes = vrf.routes(table=vrf.RT_TABLE, proto=vrf.RT_PROTO)
for route in routes:
fixed_ip = vrf.get_attribute(route['attrs'], 'RTA_DST')
LOG.info('Fixed IP to be validated: %s', fixed_ip)
if fixed_ip and (fixed_ip not in fixed_ips):
ip_route = ip_lib.IPRoute()
ip_route.route.delete_route(fixed_ip, table=vrf.RT_TABLE,
proto=vrf.RT_PROTO)
LOG.info("Stale FIB route entry removed: %s", fixed_ip)
LOG.info('Completed to clean stale devices')
self._stale_cleaned = True
@classmethod
def _is_valid_device_owner(cls, device_owner):
for permitted_device_owner in cls.PERMITTED_DEVICE_OWNERS:
if device_owner.startswith(permitted_device_owner):
return True
return False
@staticmethod
def _enable_proxy_arp(interface_name):
cmd = ['sysctl', '-b', 'net.ipv4.conf.%s.proxy_arp' % interface_name]
if utils.execute(cmd, run_as_root=True) == '0':
cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name]
ip_wrapper = ip_lib.IPWrapper()
ip_wrapper.netns.execute(cmd, check_exit_code=False)
LOG.info('Proxy ARP enabled for %s', interface_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment