# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 VMware, Inc. 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
#
#
# 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 errno
import netaddr
self.iptables_manager = None
self.router_namespace = None
"""Initialize the router on the system.
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.
:param process_monitor: The agent's process monitor instance.
"""
# Because of the way how dnsmasq works on Solaris, the length
# of datalink name cannot exceed 16 (includes terminating nul
# character). So, the linkname can only have 15 characters and
# the last two characters are set aside for '_0'. So, we only
# have 13 characters left.
dname += '_0'
# please see the comment above
dname += '_0'
if operation == 'replace':
operation = 'change'
try:
except:
operation = 'add'
route['nexthop']]
else:
assert operation == 'delete'
route['nexthop']]
break
else:
return False
fip_rules = ['pass out quick from %s to any nat-to %s static-port '
return True
"""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:
return fip_statuses
# Loop once to ensure that floating ips are configured.
for fip in floating_ips:
if fip_cidr not in existing_cidrs:
try:
continue
fip['floating_ip_address'],
# any exception occurred here should cause the floating IP
# to be set in error state
# remove the fip_cidr address if it was added
try:
except:
pass
continue
else:
[interface_name, fip_ip])
# check if existing fip has been reassigned
if fip_reassigned:
# flush rules associated with old fixed_ip and add
# new rules for the new fixed_ip
continue
# mark the status as not changed. we can't remove it
# because that's how the caller determines that it was
# removed (TODO(gmoodalb): check this)
# Clean up addresses that no longer belong on the gateway interface and
# remove the binat-to PF rule associated with them
return fip_statuses
# driver just returns if datalink and IP interface already exists
fixed_ip['ip_address'],
rules = []
# if metadata is enabled, then we need to redirect all the packets
# arriving at 169.254.169.254:80 to neutron-metadata-proxy server
# listening at self.agent_conf.metadata_port
'port 80 rdr-to 127.0.0.1 port %s label metadata_%s'
# Since we support shared router model, we need to block the new
# internal port from reaching other tenant's ports. However, if
# allow_forwarding_between_networks is set, then we need to
# allow forwarding of packets between same tenant's ports.
# finally add all the rules in one shot
if not ex_gw_port:
return
if not ex_gw_ip:
return
return
# if the external gateway is already setup for the shared router,
# then we need to add Policy Based Routing (PBR) for both inbound
# and outbound for this internal network
pbr_rules = ['pass in quick to !%s route-to {(%s %s)} label %s_in' %
if self._snat_enabled:
snat_rule = ('pass out quick from %s to any nat-to %s label %s '
# remove the anchor and tables associated with this internal port
external_dlname = self.\
pass
if not v4_subnets:
return
# add rule for metadata and broadcast
allsubnets_tblname = "all_v4_subnets"
common_aname = "common"
[common_aname])
rules = []
# don't forward broadcast packets out of the internal subnet
"""We are going to do some amount of book keeping (for later use) and
also pre-setup PF skeleton rules ahead of time to improve PF setup
time.
"""
# Process PF anchor rules for internal ports in bulk as this
# significantly improves the PF setup time. Capture the anchor
new_anchor_rules = set()
for p in new_ports:
port_id = p['id']
tenant_id = p['tenant_id']
# Capture all the subnets across all tenants and subnets
# per-tenant. We will setup PF tables for each internal network
# ahead of time
old_anchor_rules = set()
for p in old_ports:
port_id = p['id']
tenant_id = p['tenant_id']
# add an anchor rule to capture rules common amongst all the
# internal ports under 'common' anchor
if internal_ports:
# add rule for metadata and broadcast for all tenant's networks
else:
# Now that there are no internal ports, remove the common anchor
# that captures rules common amongst all the internal ports
# Since we support shared router model, we need to block the new
# internal port from reaching other tenant's ports. However, if
# allow_forwarding_between_networks is set, then we need to
# allow forwarding of packets between same tenant's ports
block_subnets = set()
allow_subnets = set()
for p in internal_ports:
tenant_id = p['tenant_id']
else:
# add table entry in the global scope
[internal_dlname, 'normal'])
if allow_subnets:
[internal_dlname, 'normal'])
if p['admin_state_up']])
p['id'] not in current_port_ids]
# updated_ports = self._get_updated_ports(self.internal_ports,
# internal_ports)
for p in new_ports:
for subnet in p['subnets']:
subnet['cidr'],
interface_name, p['mac_address'])
for p in old_ports:
for subnet in p['subnets']:
# updated_cidres = []
# 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'])
# LOG.debug("updating internal network for port %s", p)
# updated_cidrs += ip_cidrs
# 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)
# # Check if there is any pd prefix update
# for p in internal_ports:
# if p['id'] in (set(current_port_ids) & set(existing_port_ids)):
# for subnet in p.get('subnets', []):
# if ipv6_utils.is_ipv6_pd_enabled(subnet):
# old_prefix = pd.update_subnet(self.router_id,
# subnet['id'],
# subnet['cidr'])
# if old_prefix:
# self._internal_network_updated(p, subnet['id'],
# subnet['cidr'],
# old_prefix,
# updated_cidrs)
# enable_ra = True
# Enable RA
if enable_ra:
# remove any internal stale router interfaces (i.e., l3i.. VNICs)
if n.startswith(INTERNAL_DEV_PREFIX))
for port_id in current_port_ids)
for stale_dev in stale_devs:
# TODO(gmoodalb): need to do more work on ipv6 gateway
# TODO(gmoodalb): add MTU to plug()?
ex_gw_port['mac_address'],
fixed_ip['ip_address'],
# add nested anchor rule first
if gw_ip:
if 'entry exists' not in stdout:
# for each of the internal ports, add Policy Based Routing (PBR)
# rule iff ex_gw_ip is IPv4 and the internal port is IPv4
return
continue
pbr_rules = ['pass in quick to !%s route-to {(%s %s)} '
# There is nothing to do on Solaris
pass
# remove nested anchor rule first
if gw_ip:
# remove PBR rules
if self.remove_route:
'default', gw_ip]
# TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port
ex_gw_port_status = None
interface_name = None
if ex_gw_port_id:
if ex_gw_port:
if not self.ex_gw_port:
ex_gw_port_status = 'added'
ex_gw_port_status = 'updated'
ex_gw_port_status = 'removed'
# Remove any external stale router interfaces (i.e., l3e.. VNICs)
dev != interface_name]
for stale_dev in stale_devs:
# Process SNAT rules for external gateway
rules = {}
rule = ('pass out quick from %s to any nat-to %s label %s '
return rules
# Todo(gmoodalb): need this when we support address_scope
# self.process_external_port_address_scope_routing(iptables_manager)
# 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
for snat_anchor in snat_anchors:
if "/l3i" in snat_anchor:
# And add them back if the action is add_rules
# NAT rules are added only if ex_gw_port has an IPv4 address
return
fip_statuses = {}
try:
# TODO(Carl) Return after setting existing_floating_ips and
# still call update_fip_statuses?
if not ex_gw_port:
return
# Once NAT rules for floating IPs are safely in place
# configure their addresses on the external gateway port
except n_exc.FloatingIpSetupException:
# All floating IPs must be put in error state
finally:
pass
pass
OPTS = [
"networks")),
]
"""Check items in configuration files.
Check for required and invalid configuration items.
The actual values are not verified for correctness.
"""
raise SystemExit(1)
raise SystemExit(1)
# ipv6_gateway configured. Check for valid v6 link-local address.
try:
"IPv6 link-local address."),
raise SystemExit(1)
except netaddr.AddrFormatError:
raise SystemExit(1)
args = []
kwargs = {
'router_id': router_id,
'router': router,
}
return
# We don't support namespaces so only process the router associated
# with the configured agent id.
# Either ex_net_id or handle_internal_only_routers must be set
# If target_ex_net_id and ex_net_id are set they must be equal
# Double check that our single external_net_id has not changed
# by forcing a check by RPC.
else: