--- a/components/openstack/neutron/files/agent/evs_l3_agent.py Fri Feb 05 11:09:10 2016 -0800
+++ b/components/openstack/neutron/files/agent/evs_l3_agent.py Fri Feb 05 17:54:17 2016 -0500
@@ -2,7 +2,7 @@
# Copyright 2012 VMware, Inc. All rights reserved.
#
-# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2016, Oracle and/or its affiliates. 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
@@ -25,18 +25,27 @@
import netaddr
from oslo.config import cfg
+from oslo_log import log as logging
-from neutron.agent.common import config
-from neutron.agent import l3_agent
-from neutron.agent.linux import external_process
+from neutron.agent.l3 import agent as l3_agent
+from neutron.agent.l3 import router_info as router
from neutron.agent.linux import utils
from neutron.agent.solaris import interface
+from neutron.agent.solaris import ipfilters_manager
from neutron.agent.solaris import net_lib
from neutron.agent.solaris import ra
+from neutron.callbacks import events
+from neutron.callbacks import registry
+from neutron.callbacks import resources
from neutron.common import constants as l3_constants
+from neutron.common import exceptions as n_exc
from neutron.common import utils as common_utils
-from neutron.openstack.common import log as logging
+from oslo_utils import importutils
+from oslo_log import log as logging
+import neutron_vpnaas.services.vpn.agent as neutron_vpnaas
+from neutron_vpnaas.extensions import vpnaas
+from neutron_vpnaas.services.vpn import vpn_service
LOG = logging.getLogger(__name__)
INTERNAL_DEV_PREFIX = 'l3i'
@@ -44,279 +53,28 @@
FLOATING_IP_CIDR_SUFFIX = '/32'
-class EVSL3NATAgent(l3_agent.L3NATAgentWithStateReport):
- OPTS = [
- cfg.StrOpt('external_network_datalink', default='net0',
- help=_("Name of the datalink that connects to "
- "an external network.")),
- cfg.BoolOpt('allow_forwarding_between_networks', default=False,
- help=_("Allow forwarding of packets between tenant's "
- "networks")),
- ]
-
- def __init__(self, host, conf=None):
- cfg.CONF.register_opts(self.OPTS)
- cfg.CONF.register_opts(interface.OPTS)
- super(EVSL3NATAgent, self).__init__(host=host, conf=conf)
-
- def _router_added(self, router_id, router):
- ri = l3_agent.RouterInfo(router_id, None,
- self.conf.use_namespaces, router)
- self.router_info[router_id] = ri
-
- if self.conf.enable_metadata_proxy:
- self._spawn_metadata_proxy(ri.router_id, ri.ns_name)
-
- def _router_removed(self, router_id):
- ri = self.router_info.get(router_id)
- if ri is None:
- LOG.warn(_("Info for router %s were not found. "
- "Skipping router removal"), router_id)
- return
- ri.router['gw_port'] = None
- ri.router[l3_constants.INTERFACE_KEY] = []
- ri.router[l3_constants.FLOATINGIP_KEY] = []
- self.process_router(ri)
- if self.conf.enable_metadata_proxy:
- self._destroy_metadata_proxy(ri.router_id, ri.ns_name)
- ra.disable_ipv6_ra(ri.router_id)
- del self.router_info[router_id]
-
- def _get_metadata_proxy_process_manager(self, router_id, ns_name):
- return external_process.ProcessManager(
- self.conf,
- router_id,
- root_helper=None,
- namespace=ns_name)
-
- def _get_metadata_proxy_callback(self, router_id):
- """Need to override this since we need to pass the absolute
- path to neutron-ns-metadata-proxy binary.
- """
- def callback(pid_file):
- metadata_proxy_socket = cfg.CONF.metadata_proxy_socket
- proxy_cmd = ['/usr/lib/neutron/neutron-ns-metadata-proxy',
- '--pid_file=%s' % pid_file,
- '--metadata_proxy_socket=%s' % metadata_proxy_socket,
- '--router_id=%s' % router_id,
- '--state_path=%s' % self.conf.state_path,
- '--metadata_port=%s' % self.conf.metadata_port]
- proxy_cmd.extend(config.get_log_args(
- cfg.CONF, 'neutron-ns-metadata-proxy-%s.log' %
- router_id))
- return proxy_cmd
-
- return callback
+class SolarisRouterInfo(router.RouterInfo):
- def external_gateway_snat_rules(self, ex_gw_ip, internal_cidrs,
- interface_name):
- rules = []
- for cidr in internal_cidrs:
- rules.append('map %s %s -> %s/32' %
- (interface_name, cidr, ex_gw_ip))
- return rules
-
- def _handle_router_snat_rules(self, ri, ex_gw_port, internal_cidrs,
- interface_name, action):
- assert not ri.router['distributed']
-
- # Remove all the old SNAT rules
- # This is safe because if use_namespaces is set as False
- # then the agent can only configure one router, otherwise
- # each router's SNAT rules will be in their own namespace
-
- # get only the SNAT rules
- old_snat_rules = [rule for rule in ri.ipfilters_manager.ipv4['nat']
- if rule.startswith('map')]
- ri.ipfilters_manager.remove_nat_rules(old_snat_rules)
-
- # And add them back if the action is add_rules
- if action == 'add_rules' and ex_gw_port:
- # NAT rules are added only if ex_gw_port has an IPv4 address
- for ip_addr in ex_gw_port['fixed_ips']:
- ex_gw_ip = ip_addr['ip_address']
- if netaddr.IPAddress(ex_gw_ip).version == 4:
- rules = self.external_gateway_snat_rules(ex_gw_ip,
- internal_cidrs,
- interface_name)
- ri.ipfilters_manager.add_nat_rules(rules)
- break
-
- @common_utils.exception_logger()
- def process_router(self, ri):
- # TODO(mrsmith) - we shouldn't need to check here
- if 'distributed' not in ri.router:
- ri.router['distributed'] = False
- ex_gw_port = self._get_ex_gw_port(ri)
- internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, [])
- existing_port_ids = set([p['id'] for p in ri.internal_ports])
- current_port_ids = set([p['id'] for p in internal_ports
- if p['admin_state_up']])
- new_ports = [p for p in internal_ports if
- p['id'] in current_port_ids and
- p['id'] not in existing_port_ids]
- old_ports = [p for p in ri.internal_ports if
- p['id'] not in current_port_ids]
- new_ipv6_port = False
- old_ipv6_port = False
- for p in new_ports:
- self._set_subnet_info(p)
- self.internal_network_added(ri, p)
- ri.internal_ports.append(p)
- if (not new_ipv6_port and
- netaddr.IPNetwork(p['subnet']['cidr']).version == 6):
- new_ipv6_port = True
-
- for p in old_ports:
- self.internal_network_removed(ri, p)
- ri.internal_ports.remove(p)
- if (not old_ipv6_port and
- netaddr.IPNetwork(p['subnet']['cidr']).version == 6):
- old_ipv6_port = True
-
- if new_ipv6_port or old_ipv6_port:
- # refresh ndpd daemon after filling in ndpd.conf
- # with the right entries
- ra.enable_ipv6_ra(ri.router_id,
- internal_ports,
- self.get_internal_device_name)
+ def __init__(self, router_id, router, agent_conf, interface_driver,
+ use_ipv6=False):
+ super(SolarisRouterInfo, self).__init__(router_id, router, agent_conf,
+ interface_driver, use_ipv6)
+ self.ipfilters_manager = ipfilters_manager.IPfiltersManager()
+ self.iptables_manager = None
+ self.remove_route = False
- # remove any internal stale router interfaces (i.e., l3i.. VNICs)
- existing_devices = net_lib.Datalink.show_vnic()
- current_internal_devs = set([n for n in existing_devices
- if n.startswith(INTERNAL_DEV_PREFIX)])
- current_port_devs = set([self.get_internal_device_name(id) for
- id in current_port_ids])
- stale_devs = current_internal_devs - current_port_devs
- for stale_dev in stale_devs:
- LOG.debug(_('Deleting stale internal router device: %s'),
- stale_dev)
- self.driver.fini_l3(stale_dev)
- self.driver.unplug(stale_dev)
-
- # TODO(salv-orlando): RouterInfo would be a better place for
- # this logic too
- ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or
- ri.ex_gw_port and ri.ex_gw_port['id'])
-
- interface_name = None
- if ex_gw_port_id:
- interface_name = self.get_external_device_name(ex_gw_port_id)
- if ex_gw_port:
- def _gateway_ports_equal(port1, port2):
- def _get_filtered_dict(d, ignore):
- return dict((k, v) for k, v in d.iteritems()
- if k not in ignore)
-
- keys_to_ignore = set(['binding:host_id'])
- port1_filtered = _get_filtered_dict(port1, keys_to_ignore)
- port2_filtered = _get_filtered_dict(port2, keys_to_ignore)
- return port1_filtered == port2_filtered
-
- self._set_subnet_info(ex_gw_port)
- if not ri.ex_gw_port:
- self.external_gateway_added(ri, ex_gw_port, interface_name)
- elif not _gateway_ports_equal(ex_gw_port, ri.ex_gw_port):
- self.external_gateway_updated(ri, ex_gw_port, interface_name)
- elif not ex_gw_port and ri.ex_gw_port:
- self.external_gateway_removed(ri, ri.ex_gw_port, interface_name)
-
- # Remove any external stale router interfaces (i.e., l3e.. VNICs)
- stale_devs = [dev for dev in existing_devices
- if dev.startswith(EXTERNAL_DEV_PREFIX)
- and dev != interface_name]
- for stale_dev in stale_devs:
- LOG.debug(_('Deleting stale external router device: %s'),
- stale_dev)
- self.driver.fini_l3(stale_dev)
- self.driver.unplug(stale_dev)
-
- # Process static routes for router
- self.routes_updated(ri)
-
- # Process SNAT rules for external gateway
- if (not ri.router['distributed'] or
- ex_gw_port and self.get_gw_port_host(ri.router) == self.host):
- # Get IPv4 only internal CIDRs
- internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports
- if netaddr.IPNetwork(p['ip_cidr']).version == 4]
- ri.perform_snat_action(self._handle_router_snat_rules,
- internal_cidrs, interface_name)
+ def initialize(self, process_monitor):
+ """Initialize the router on the system.
- # Process SNAT/DNAT rules for floating IPs
- fip_statuses = {}
- if ex_gw_port:
- existing_floating_ips = ri.floating_ips
- fip_statuses = self.process_router_floating_ips(ri, ex_gw_port)
- # Identify floating IPs which were disabled
- ri.floating_ips = set(fip_statuses.keys())
- for fip_id in existing_floating_ips - ri.floating_ips:
- fip_statuses[fip_id] = l3_constants.FLOATINGIP_STATUS_DOWN
- # Update floating IP status on the neutron server
- self.plugin_rpc.update_floatingip_statuses(
- self.context, ri.router_id, fip_statuses)
-
- # Update ex_gw_port and enable_snat on the router info cache
- ri.ex_gw_port = ex_gw_port
- ri.enable_snat = ri.router.get('enable_snat')
-
- def process_router_floating_ips(self, ri, ex_gw_port):
- """Configure the router's floating IPs
- Configures floating ips using ipnat(1m) on the router's gateway device.
-
- Cleans up floating ips that should not longer be configured.
- """
- ifname = self.get_external_device_name(ex_gw_port['id'])
- ipintf = net_lib.IPInterface(ifname)
- ipaddr_list = ipintf.ipaddr_list()['static']
-
- existing_cidrs = set(ipaddr_list)
- new_cidrs = set()
-
- existing_nat_rules = [nat_rule for nat_rule in
- ri.ipfilters_manager.ipv4['nat']]
- new_nat_rules = []
+ This differs from __init__ in that this method actually affects the
+ system creating namespaces, starting processes, etc. The other merely
+ initializes the python object. This separates in-memory object
+ initialization from methods that actually go do stuff to the system.
- # Loop once to ensure that floating ips are configured.
- fip_statuses = {}
- for fip in ri.router.get(l3_constants.FLOATINGIP_KEY, []):
- fip_ip = fip['floating_ip_address']
- fip_cidr = str(fip_ip) + FLOATING_IP_CIDR_SUFFIX
- new_cidrs.add(fip_cidr)
- fixed_cidr = str(fip['fixed_ip_address']) + '/32'
- nat_rule = 'bimap %s %s -> %s' % (ifname, fixed_cidr, fip_cidr)
-
- if fip_cidr not in existing_cidrs:
- try:
- ipintf.create_address(fip_cidr)
- ri.ipfilters_manager.add_nat_rules([nat_rule])
- except Exception as err:
- # TODO(gmoodalb): If we fail in add_nat_rules(), then
- # we need to remove the fip_cidr address
-
- # any exception occurred here should cause the floating IP
- # to be set in error state
- fip_statuses[fip['id']] = (
- l3_constants.FLOATINGIP_STATUS_ERROR)
- LOG.warn(_("Unable to configure IP address for "
- "floating IP: %s: %s") % (fip['id'], err))
- continue
- fip_statuses[fip['id']] = (
- l3_constants.FLOATINGIP_STATUS_ACTIVE)
- new_nat_rules.append(nat_rule)
-
- # remove all the old NAT rules
- old_nat_rules = list(set(existing_nat_rules) - set(new_nat_rules))
- # Filter out 'bimap' NAT rules as we don't want to remove NAT rules
- # that were added for Metadata server
- old_nat_rules = [rule for rule in old_nat_rules if "bimap" in rule]
- ri.ipfilters_manager.remove_nat_rules(old_nat_rules)
-
- # Clean up addresses that no longer belong on the gateway interface.
- for ip_cidr in existing_cidrs - new_cidrs:
- if ip_cidr.endswith(FLOATING_IP_CIDR_SUFFIX):
- ipintf.delete_address(ip_cidr)
- return fip_statuses
+ :param process_monitor: The agent's process monitor instance.
+ """
+ self.process_monitor = process_monitor
+ self.radvd = ra.NDPD(self.router_id, self.get_internal_device_name)
def get_internal_device_name(self, port_id):
# Because of the way how dnsmasq works on Solaris, the length
@@ -334,7 +92,272 @@
dname += '_0'
return dname.replace('-', '_')
- def external_gateway_added(self, ri, ex_gw_port, external_dlname):
+ def routes_updated(self):
+ pass
+
+ def _get_existing_devices(self):
+ return net_lib.Datalink.show_vnic()
+
+ def internal_network_added(self, port):
+ internal_dlname = self.get_internal_device_name(port['id'])
+ # driver just returns if datalink and IP interface already exists
+ self.driver.plug(port['tenant_id'], port['network_id'], port['id'],
+ internal_dlname)
+ ip_cidrs = common_utils.fixed_ip_cidrs(port['fixed_ips'])
+ self.driver.init_l3(internal_dlname, ip_cidrs)
+
+ # Since we support shared router model, we need to block the new
+ # internal port from reaching other tenant's ports
+ block_pname = self._get_ippool_name(port['mac_address'])
+ self.ipfilters_manager.add_ippool(block_pname, None)
+ if self.agent_conf.allow_forwarding_between_networks:
+ # If allow_forwarding_between_networks is set, then we need to
+ # allow forwarding of packets between same tenant's ports.
+ allow_pname = self._get_ippool_name(port['mac_address'], '0')
+ self.ipfilters_manager.add_ippool(allow_pname, None)
+
+ # walk through the other internal ports and retrieve their
+ # cidrs and at the same time add the new internal port's
+ # cidr to them
+ port_subnet = port['subnets'][0]['cidr']
+ block_subnets = []
+ allow_subnets = []
+ for internal_port in self.internal_ports:
+ if internal_port['mac_address'] == port['mac_address']:
+ continue
+ if (self.agent_conf.allow_forwarding_between_networks and
+ internal_port['tenant_id'] == port['tenant_id']):
+ allow_subnets.append(internal_port['subnets'][0]['cidr'])
+ # we need to add the port's subnet to this internal_port's
+ # allowed_subnet_pool
+ iport_allow_pname = \
+ self._get_ippool_name(internal_port['mac_address'], '0')
+ self.ipfilters_manager.add_ippool(iport_allow_pname,
+ [port_subnet])
+ else:
+ block_subnets.append(internal_port['subnets'][0]['cidr'])
+ iport_block_pname = \
+ self._get_ippool_name(internal_port['mac_address'])
+ self.ipfilters_manager.add_ippool(iport_block_pname,
+ [port_subnet])
+ # update the new port's pool with other ports' subnet
+ self.ipfilters_manager.add_ippool(block_pname, block_subnets)
+ if self.agent_conf.allow_forwarding_between_networks:
+ self.ipfilters_manager.add_ippool(allow_pname, allow_subnets)
+
+ # now setup the IPF rules
+ rules = ['block in quick on %s from %s to pool/%d' %
+ (internal_dlname, port_subnet, block_pname)]
+ # pass in packets between networks that belong to same tenant
+ if self.agent_conf.allow_forwarding_between_networks:
+ rules.append('pass in quick on %s from %s to pool/%d' %
+ (internal_dlname, port_subnet, allow_pname))
+ # if the external gateway is already setup for the shared router,
+ # then we need to add Policy Based Routing (PBR) for this internal
+ # network
+ ex_gw_port = self.ex_gw_port
+ ex_gw_ip = (ex_gw_port['subnets'][0]['gateway_ip']
+ if ex_gw_port else None)
+ if ex_gw_ip:
+ external_dlname = self.get_external_device_name(ex_gw_port['id'])
+ rules.append('pass in on %s to %s:%s from any to !%s' %
+ (internal_dlname, external_dlname, ex_gw_ip,
+ port_subnet))
+
+ ipversion = netaddr.IPNetwork(port_subnet).version
+ self.ipfilters_manager.add_ipf_rules(rules, ipversion)
+ if self.agent_conf.enable_metadata_proxy and ipversion == 4:
+ rdr_rule = ['rdr %s 169.254.169.254/32 port 80 -> %s port %d tcp' %
+ (internal_dlname, port['fixed_ips'][0]['ip_address'],
+ self.agent_conf.metadata_port)]
+ self.ipfilters_manager.add_nat_rules(rdr_rule)
+
+ def internal_network_removed(self, port):
+ internal_dlname = self.get_internal_device_name(port['id'])
+ port_subnet = port['subnets'][0]['cidr']
+ # remove all the IP filter rules that we added during
+ # internal network addition
+ block_pname = self._get_ippool_name(port['mac_address'])
+ rules = ['block in quick on %s from %s to pool/%d' %
+ (internal_dlname, port_subnet, block_pname)]
+ if self.agent_conf.allow_forwarding_between_networks:
+ allow_pname = self._get_ippool_name(port['mac_address'], '0')
+ rules.append('pass in quick on %s from %s to pool/%d' %
+ (internal_dlname, port_subnet, allow_pname))
+
+ # remove all the IP filter rules that we added during
+ # external network addition
+ ex_gw_port = self.ex_gw_port
+ ex_gw_ip = (ex_gw_port['subnets'][0]['gateway_ip']
+ if ex_gw_port else None)
+ if ex_gw_ip:
+ external_dlname = self.get_external_device_name(ex_gw_port['id'])
+ rules.append('pass in on %s to %s:%s from any to !%s' %
+ (internal_dlname, external_dlname, ex_gw_ip,
+ port_subnet))
+ ipversion = netaddr.IPNetwork(port['subnets'][0]['cidr']).version
+ self.ipfilters_manager.remove_ipf_rules(rules, ipversion)
+
+ # remove the ippool
+ self.ipfilters_manager.remove_ippool(block_pname, None)
+ if self.agent_conf.allow_forwarding_between_networks:
+ self.ipfilters_manager.remove_ippool(allow_pname, None)
+
+ for internal_port in self.internal_ports:
+ if (self.agent_conf.allow_forwarding_between_networks and
+ internal_port['tenant_id'] == port['tenant_id']):
+ iport_allow_pname = \
+ self._get_ippool_name(internal_port['mac_address'], '0')
+ self.ipfilters_manager.remove_ippool(iport_allow_pname,
+ [port_subnet])
+ else:
+ iport_block_pname = \
+ self._get_ippool_name(internal_port['mac_address'])
+ self.ipfilters_manager.remove_ippool(iport_block_pname,
+ [port_subnet])
+ if self.agent_conf.enable_metadata_proxy and ipversion == 4:
+ rdr_rule = ['rdr %s 169.254.169.254/32 port 80 -> %s port %d tcp' %
+ (internal_dlname, port['fixed_ips'][0]['ip_address'],
+ self.agent_conf.metadata_port)]
+ self.ipfilters_manager.remove_nat_rules(rdr_rule)
+
+ if net_lib.Datalink.datalink_exists(internal_dlname):
+ self.driver.fini_l3(internal_dlname)
+ self.driver.unplug(internal_dlname)
+
+ def _process_internal_ports(self):
+ existing_port_ids = set([p['id'] for p in self.internal_ports])
+
+ internal_ports = self.router.get(l3_constants.INTERFACE_KEY, [])
+ current_port_ids = set([p['id'] for p in internal_ports
+ if p['admin_state_up']])
+
+ new_port_ids = current_port_ids - existing_port_ids
+ new_ports = [p for p in internal_ports if p['id'] in new_port_ids]
+ old_ports = [p for p in self.internal_ports if
+ p['id'] not in current_port_ids]
+# updated_ports = self._get_updated_ports(self.internal_ports,
+# internal_ports)
+
+ enable_ra = False
+ for p in new_ports:
+ self.internal_network_added(p)
+ self.internal_ports.append(p)
+ enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
+
+ for p in old_ports:
+ self.internal_network_removed(p)
+ self.internal_ports.remove(p)
+ enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
+
+# if updated_ports:
+# for index, p in enumerate(internal_ports):
+# if not updated_ports.get(p['id']):
+# continue
+# self.internal_ports[index] = updated_ports[p['id']]
+# interface_name = self.get_internal_device_name(p['id'])
+# ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips'])
+# self.driver.init_l3(interface_name, ip_cidrs=ip_cidrs,
+# namespace=self.ns_name)
+# enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
+
+ # Enable RA
+ if enable_ra:
+ self.radvd.enable(internal_ports)
+
+ # remove any internal stale router interfaces (i.e., l3i.. VNICs)
+ existing_devices = self._get_existing_devices()
+ current_internal_devs = set(n for n in existing_devices
+ if n.startswith(INTERNAL_DEV_PREFIX))
+ current_port_devs = set(self.get_internal_device_name(port_id)
+ for port_id in current_port_ids)
+ stale_devs = current_internal_devs - current_port_devs
+ for stale_dev in stale_devs:
+ LOG.debug(_('Deleting stale internal router device: %s'),
+ stale_dev)
+ self.driver.fini_l3(stale_dev)
+ self.driver.unplug(stale_dev)
+
+ def _get_ippool_name(self, mac_address, suffix=None):
+ # Generate a unique-name for ippool(1m) from that last 3
+ # bytes of mac-address. It is called pool name, but it is
+ # actually a 32 bit integer
+ name = mac_address.split(':')[3:]
+ if suffix:
+ name.append(suffix)
+ return int("".join(name), 16)
+
+ def process_floating_ip_addresses(self, interface_name):
+ """Configure IP addresses on router's external gateway interface.
+
+ Ensures addresses for existing floating IPs and cleans up
+ those that should not longer be configured.
+ """
+
+ fip_statuses = {}
+ if interface_name is None:
+ LOG.debug('No Interface for floating IPs router: %s',
+ self.router['id'])
+ return fip_statuses
+
+ ipintf = net_lib.IPInterface(interface_name)
+ ipaddr_list = ipintf.ipaddr_list()['static']
+
+ existing_cidrs = set(ipaddr_list)
+ new_cidrs = set()
+
+ existing_nat_rules = [nat_rule for nat_rule in
+ self.ipfilters_manager.ipv4['nat']]
+ new_nat_rules = []
+
+ floating_ips = self.get_floating_ips()
+ # Loop once to ensure that floating ips are configured.
+ for fip in floating_ips:
+ fip_ip = fip['floating_ip_address']
+ fip_cidr = str(fip_ip) + FLOATING_IP_CIDR_SUFFIX
+ new_cidrs.add(fip_cidr)
+ fixed_cidr = str(fip['fixed_ip_address']) + '/32'
+ nat_rule = 'bimap %s %s -> %s' % (interface_name, fixed_cidr,
+ fip_cidr)
+
+ if fip_cidr not in existing_cidrs:
+ try:
+ ipintf.create_address(fip_cidr)
+ self.ipfilters_manager.add_nat_rules([nat_rule])
+ except Exception as err:
+ # TODO(gmoodalb): If we fail in add_nat_rules(), then
+ # we need to remove the fip_cidr address
+
+ # any exception occurred here should cause the floating IP
+ # to be set in error state
+ fip_statuses[fip['id']] = (
+ l3_constants.FLOATINGIP_STATUS_ERROR)
+ LOG.warn(_("Unable to configure IP address for "
+ "floating IP: %s: %s") % (fip['id'], err))
+ continue
+ fip_statuses[fip['id']] = (
+ l3_constants.FLOATINGIP_STATUS_ACTIVE)
+ LOG.debug("Floating ip %(id)s added, status %(status)s",
+ {'id': fip['id'],
+ 'status': fip_statuses.get(fip['id'])})
+
+ new_nat_rules.append(nat_rule)
+
+ # remove all the old NAT rules
+ old_nat_rules = list(set(existing_nat_rules) - set(new_nat_rules))
+ # Filter out 'bimap' NAT rules as we don't want to remove NAT rules
+ # that were added for Metadata server
+ old_nat_rules = [rule for rule in old_nat_rules if "bimap" in rule]
+ self.ipfilters_manager.remove_nat_rules(old_nat_rules)
+
+ # Clean up addresses that no longer belong on the gateway interface.
+ for ip_cidr in existing_cidrs - new_cidrs:
+ if ip_cidr.endswith(FLOATING_IP_CIDR_SUFFIX):
+ ipintf.delete_address(ip_cidr)
+ return fip_statuses
+
+ # Todo(gmoodalb): need to do more work on ipv6 gateway
+ def external_gateway_added(self, ex_gw_port, external_dlname):
if not net_lib.Datalink.datalink_exists(external_dlname):
dl = net_lib.Datalink(external_dlname)
@@ -357,7 +380,7 @@
"create an external gateway port"))
return
elif (l2type == 'vlan' and
- self.conf.get("external_network_datalink", None)):
+ self.agent_conf.get("external_network_datalink", None)):
LOG.warning(_("external_network_datalink is deprecated in "
"Juno and will be removed in the next release "
"of Solaris OpenStack. Please use the evsadm "
@@ -365,52 +388,53 @@
"uplink-port for an external network"))
# proceed with the old-style of doing things
mac_address = ex_gw_port['mac_address']
- dl.create_vnic(self.conf.external_network_datalink,
+ dl.create_vnic(self.agent_conf.external_network_datalink,
mac_address=mac_address, vid=vid)
else:
self.driver.plug(ex_gw_port['tenant_id'],
ex_gw_port['network_id'],
ex_gw_port['id'], external_dlname)
- self.driver.init_l3(external_dlname, [ex_gw_port['ip_cidr']])
+ ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+ self.driver.init_l3(external_dlname, ip_cidrs)
- # TODO(gmoodalb): wrap route(1m) command within a class in net_lib.py
- gw_ip = ex_gw_port['subnet']['gateway_ip']
+ gw_ip = ex_gw_port['subnets'][0]['gateway_ip']
if gw_ip:
cmd = ['/usr/bin/pfexec', '/usr/sbin/route', 'add', 'default',
gw_ip]
stdout = utils.execute(cmd, extra_ok_codes=[errno.EEXIST])
- ri.remove_route = True
- if 'entry exists' in stdout:
- ri.remove_route = False
+ if 'entry exists' not in stdout:
+ self.remove_route = True
# for each of the internal ports, add Policy Based
# Routing (PBR) rule
- for port in ri.internal_ports:
+ for port in self.internal_ports:
internal_dlname = self.get_internal_device_name(port['id'])
rules = ['pass in on %s to %s:%s from any to !%s' %
(internal_dlname, external_dlname, gw_ip,
- port['subnet']['cidr'])]
- ipversion = netaddr.IPNetwork(port['subnet']['cidr']).version
- ri.ipfilters_manager.add_ipf_rules(rules, ipversion)
+ port['subnets'][0]['cidr'])]
+ ipversion = \
+ netaddr.IPNetwork(port['subnets'][0]['cidr']).version
+ self.ipfilters_manager.add_ipf_rules(rules, ipversion)
- def external_gateway_updated(self, ri, ex_gw_port, external_dlname):
+ def external_gateway_updated(self, ex_gw_port, external_dlname):
# There is nothing to do on Solaris
pass
- def external_gateway_removed(self, ri, ex_gw_port, external_dlname):
- gw_ip = ex_gw_port['subnet']['gateway_ip']
+ def external_gateway_removed(self, ex_gw_port, external_dlname):
+ gw_ip = ex_gw_port['subnets'][0]['gateway_ip']
if gw_ip:
# remove PBR rules
- for port in ri.internal_ports:
+ for port in self.internal_ports:
internal_dlname = self.get_internal_device_name(port['id'])
rules = ['pass in on %s to %s:%s from any to !%s' %
(internal_dlname, external_dlname, gw_ip,
- port['subnet']['cidr'])]
- ipversion = netaddr.IPNetwork(port['subnet']['cidr']).version
- ri.ipfilters_manager.remove_ipf_rules(rules, ipversion)
+ port['subnets'][0]['cidr'])]
+ ipversion = \
+ netaddr.IPNetwork(port['subnets'][0]['cidr']).version
+ self.ipfilters_manager.remove_ipf_rules(rules, ipversion)
- if ri.remove_route:
+ if self.remove_route:
cmd = ['/usr/bin/pfexec', '/usr/sbin/route', 'delete',
'default', gw_ip]
utils.execute(cmd, check_exit_code=False)
@@ -419,149 +443,135 @@
self.driver.fini_l3(external_dlname)
self.driver.unplug(external_dlname)
- def _get_ippool_name(self, mac_address, suffix=None):
- # Generate a unique-name for ippool(1m) from that last 3
- # bytes of mac-address. It is called pool name, but it is
- # actually a 32 bit integer
- name = mac_address.split(':')[3:]
- if suffix:
- name.append(suffix)
- return int("".join(name), 16)
+ def _process_external_gateway(self, ex_gw_port):
+ # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port
+ ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or
+ self.ex_gw_port and self.ex_gw_port['id'])
- def internal_network_added(self, ri, port):
- internal_dlname = self.get_internal_device_name(port['id'])
- # driver just returns if datalink and IP interface already exists
- self.driver.plug(port['tenant_id'], port['network_id'], port['id'],
- internal_dlname)
- self.driver.init_l3(internal_dlname, [port['ip_cidr']])
+ interface_name = None
+ if ex_gw_port_id:
+ interface_name = self.get_external_device_name(ex_gw_port_id)
+ if ex_gw_port:
+ def _gateway_ports_equal(port1, port2):
+ def _get_filtered_dict(d, ignore):
+ return dict((k, v) for k, v in d.iteritems()
+ if k not in ignore)
- # Since we support shared router model, we need to block the new
- # internal port from reaching other tenant's ports
- block_pname = self._get_ippool_name(port['mac_address'])
- ri.ipfilters_manager.add_ippool(block_pname, None)
- if self.conf.allow_forwarding_between_networks:
- # If allow_forwarding_between_networks is set, then we need to
- # allow forwarding of packets between same tenant's ports.
- allow_pname = self._get_ippool_name(port['mac_address'], '0')
- ri.ipfilters_manager.add_ippool(allow_pname, None)
+ keys_to_ignore = set(['binding:host_id'])
+ port1_filtered = _get_filtered_dict(port1, keys_to_ignore)
+ port2_filtered = _get_filtered_dict(port2, keys_to_ignore)
+ return port1_filtered == port2_filtered
+
+ if not self.ex_gw_port:
+ self.external_gateway_added(ex_gw_port, interface_name)
+ elif not _gateway_ports_equal(ex_gw_port, self.ex_gw_port):
+ self.external_gateway_updated(ex_gw_port, interface_name)
+ elif not ex_gw_port and self.ex_gw_port:
+ self.external_gateway_removed(self.ex_gw_port, interface_name)
- # walk through the other internal ports and retrieve their
- # cidrs and at the same time add the new internal port's
- # cidr to them
- port_subnet = port['subnet']['cidr']
- block_subnets = []
- allow_subnets = []
- for internal_port in ri.internal_ports:
- if internal_port['mac_address'] == port['mac_address']:
- continue
- if (self.conf.allow_forwarding_between_networks and
- internal_port['tenant_id'] == port['tenant_id']):
- allow_subnets.append(internal_port['subnet']['cidr'])
- # we need to add the port's subnet to this internal_port's
- # allowed_subnet_pool
- iport_allow_pname = \
- self._get_ippool_name(internal_port['mac_address'], '0')
- ri.ipfilters_manager.add_ippool(iport_allow_pname,
- [port_subnet])
- else:
- block_subnets.append(internal_port['subnet']['cidr'])
- iport_block_pname = \
- self._get_ippool_name(internal_port['mac_address'])
- ri.ipfilters_manager.add_ippool(iport_block_pname,
- [port_subnet])
- # update the new port's pool with other ports' subnet
- ri.ipfilters_manager.add_ippool(block_pname, block_subnets)
- if self.conf.allow_forwarding_between_networks:
- ri.ipfilters_manager.add_ippool(allow_pname, allow_subnets)
+ # Remove any external stale router interfaces (i.e., l3e.. VNICs)
+ existing_devices = self._get_existing_devices()
+ stale_devs = [dev for dev in existing_devices
+ if dev.startswith(EXTERNAL_DEV_PREFIX) and
+ dev != interface_name]
+ for stale_dev in stale_devs:
+ LOG.debug(_('Deleting stale external router device: %s'),
+ stale_dev)
+ self.driver.fini_l3(stale_dev)
+ self.driver.unplug(stale_dev)
+
+ # Process SNAT rules for external gateway
+ self.perform_snat_action(self._handle_router_snat_rules,
+ interface_name)
- # now setup the IPF rules
- rules = ['block in quick on %s from %s to pool/%d' %
- (internal_dlname, port_subnet, block_pname)]
- # pass in packets between networks that belong to same tenant
- if self.conf.allow_forwarding_between_networks:
- rules.append('pass in quick on %s from %s to pool/%d' %
- (internal_dlname, port_subnet, allow_pname))
- # if the external gateway is already setup for the shared router,
- # then we need to add Policy Based Routing (PBR) for this internal
- # network
- ex_gw_port = ri.ex_gw_port
- ex_gw_ip = (ex_gw_port['subnet']['gateway_ip'] if ex_gw_port else None)
- if ex_gw_ip:
- external_dlname = self.get_external_device_name(ex_gw_port['id'])
- rules.append('pass in on %s to %s:%s from any to !%s' %
- (internal_dlname, external_dlname, ex_gw_ip,
- port_subnet))
+ def external_gateway_snat_rules(self, ex_gw_ip, interface_name):
+ rules = []
+ ip_cidrs = []
+ for port in self.internal_ports:
+ if netaddr.IPNetwork(port['subnets'][0]['cidr']).version == 4:
+ ip_cidrs.extend(common_utils.fixed_ip_cidrs(port['fixed_ips']))
+
+ for ip_cidr in ip_cidrs:
+ rules.append('map %s %s -> %s/32' %
+ (interface_name, ip_cidr, ex_gw_ip))
+ return rules
+
+ def _handle_router_snat_rules(self, ex_gw_port, interface_name, action):
+ # Remove all the old SNAT rules
+ # This is safe because if use_namespaces is set as False
+ # then the agent can only configure one router, otherwise
+ # each router's SNAT rules will be in their own namespace
+
+ # get only the SNAT rules
+ old_snat_rules = [rule for rule in self.ipfilters_manager.ipv4['nat']
+ if rule.startswith('map')]
+ self.ipfilters_manager.remove_nat_rules(old_snat_rules)
- ipversion = netaddr.IPNetwork(port_subnet).version
- ri.ipfilters_manager.add_ipf_rules(rules, ipversion)
+ # And add them back if the action is add_rules
+ if action == 'add_rules' and ex_gw_port:
+ # NAT rules are added only if ex_gw_port has an IPv4 address
+ for ip_addr in ex_gw_port['fixed_ips']:
+ ex_gw_ip = ip_addr['ip_address']
+ if netaddr.IPAddress(ex_gw_ip).version == 4:
+ rules = self.external_gateway_snat_rules(ex_gw_ip,
+ interface_name)
+ self.ipfilters_manager.add_nat_rules(rules)
+ break
- # if metadata proxy is enabled, then add the necessary
- # IP NAT rules to forward the metadata requests to the
- # metadata proxy server
- if self.conf.enable_metadata_proxy and ipversion == 4:
- # TODO(gmoodalb): when IP Filter allows redirection of packets
- # to loopback IP address, then we need to add an IPF rule allowing
- # only packets destined to 127.0.0.1:9697 to
- # neutron-ns-metadata-proxy server
- rules = ['rdr %s 169.254.169.254/32 port 80 -> %s port %d tcp' %
- (internal_dlname, port['fixed_ips'][0]['ip_address'],
- self.conf.metadata_port)]
- ri.ipfilters_manager.add_nat_rules(rules)
+ def process_external(self, agent):
+ existing_floating_ips = self.floating_ips
+ try:
+ ex_gw_port = self.get_ex_gw_port()
+ self._process_external_gateway(ex_gw_port)
+ # TODO(Carl) Return after setting existing_floating_ips and
+ # still call update_fip_statuses?
+ if not ex_gw_port:
+ return
- def internal_network_removed(self, ri, port):
- internal_dlname = self.get_internal_device_name(port['id'])
- port_subnet = port['subnet']['cidr']
- # remove all the IP filter rules that we added during
- # internal network addition
- block_pname = self._get_ippool_name(port['mac_address'])
- rules = ['block in quick on %s from %s to pool/%d' %
- (internal_dlname, port_subnet, block_pname)]
- if self.conf.allow_forwarding_between_networks:
- allow_pname = self._get_ippool_name(port['mac_address'], '0')
- rules.append('pass in quick on %s from %s to pool/%d' %
- (internal_dlname, port_subnet, allow_pname))
+ # Once NAT rules for floating IPs are safely in place
+ # configure their addresses on the external gateway port
+ interface_name = self.get_external_device_name(ex_gw_port['id'])
+ fip_statuses = self.configure_fip_addresses(interface_name)
+ except (n_exc.FloatingIpSetupException,
+ n_exc.IpTablesApplyException) as e:
+ # All floating IPs must be put in error state
+ LOG.exception(e)
+ fip_statuses = self.put_fips_in_error_state()
+
+ agent.update_fip_statuses(self, existing_floating_ips, fip_statuses)
+
- # remove all the IP filter rules that we added during
- # external network addition
- ex_gw_port = ri.ex_gw_port
- ex_gw_ip = (ex_gw_port['subnet']['gateway_ip'] if ex_gw_port else None)
- if ex_gw_ip:
- external_dlname = self.get_external_device_name(ex_gw_port['id'])
- rules.append('pass in on %s to %s:%s from any to !%s' %
- (internal_dlname, external_dlname, ex_gw_ip,
- port_subnet))
- ipversion = netaddr.IPNetwork(port['subnet']['cidr']).version
- ri.ipfilters_manager.remove_ipf_rules(rules, ipversion)
-
- # remove the ippool
- ri.ipfilters_manager.remove_ippool(block_pname, None)
- if self.conf.allow_forwarding_between_networks:
- ri.ipfilters_manager.remove_ippool(allow_pname, None)
+class EVSL3NATAgent(l3_agent.L3NATAgentWithStateReport):
+ OPTS = [
+ cfg.StrOpt('external_network_datalink', default='net0',
+ help=_("Name of the datalink that connects to "
+ "an external network.")),
+ cfg.BoolOpt('allow_forwarding_between_networks', default=False,
+ help=_("Allow forwarding of packets between tenant's "
+ "networks")),
+ ]
- for internal_port in ri.internal_ports:
- if (self.conf.allow_forwarding_between_networks and
- internal_port['tenant_id'] == port['tenant_id']):
- iport_allow_pname = \
- self._get_ippool_name(internal_port['mac_address'], '0')
- ri.ipfilters_manager.remove_ippool(iport_allow_pname,
- [port_subnet])
- else:
- iport_block_pname = \
- self._get_ippool_name(internal_port['mac_address'])
- ri.ipfilters_manager.remove_ippool(iport_block_pname,
- [port_subnet])
+ def __init__(self, host, conf=None):
+ cfg.CONF.register_opts(self.OPTS)
+ cfg.CONF.register_opts(interface.OPTS)
+ super(EVSL3NATAgent, self).__init__(host=host, conf=conf)
+ cfg.CONF.register_opts(neutron_vpnaas.vpn_agent_opts, 'vpnagent')
+ self.service = vpn_service.VPNService(self)
+ self.device_drivers = self.service.load_device_drivers(host)
- # if metadata proxy is enabled, then remove the IP NAT rules that
- # were added while adding the internal network
- if self.conf.enable_metadata_proxy and ipversion == 4:
- rules = ['rdr %s 169.254.169.254/32 port 80 -> %s port %d tcp' %
- (internal_dlname, port['fixed_ips'][0]['ip_address'],
- self.conf.metadata_port)]
- ri.ipfilters_manager.remove_nat_rules(rules)
+ def _router_added(self, router_id, router):
+ args = []
+ kwargs = {
+ 'router_id': router_id,
+ 'router': router,
+ 'use_ipv6': self.use_ipv6,
+ 'agent_conf': self.conf,
+ 'interface_driver': self.driver,
+ }
+ ri = SolarisRouterInfo(*args, **kwargs)
+ registry.notify(resources.ROUTER, events.BEFORE_CREATE,
+ self, router=ri)
- if net_lib.Datalink.datalink_exists(internal_dlname):
- self.driver.fini_l3(internal_dlname)
- self.driver.unplug(internal_dlname)
+ self.router_info[router_id] = ri
- def routes_updated(self, ri):
- pass
+ ri.initialize(self.process_monitor)