1 # Copyright (c) 2014 OpenStack Foundation
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
17 from oslo_log import log as logging
19 from neutron._i18n import _, _LE, _LW
20 from neutron.agent.l3 import namespaces
21 from neutron.agent.linux import ip_lib
22 from neutron.agent.linux import iptables_manager
23 from neutron.agent.linux import ra
24 from neutron.common import constants as l3_constants
25 from neutron.common import exceptions as n_exc
26 from neutron.common import ipv6_utils
27 from neutron.common import utils as common_utils
28 from neutron.ipam import utils as ipam_utils
29 from neutron.agent.linux.interface import OVSInterfaceDriver
31 LOG = logging.getLogger(__name__)
32 INTERNAL_DEV_PREFIX = namespaces.INTERNAL_DEV_PREFIX
33 EXTERNAL_DEV_PREFIX = namespaces.EXTERNAL_DEV_PREFIX
35 FLOATINGIP_STATUS_NOCHANGE = object()
36 ADDRESS_SCOPE_MARK_MASK = "0xffff0000"
37 ADDRESS_SCOPE_MARK_ID_MIN = 1024
38 ADDRESS_SCOPE_MARK_ID_MAX = 2048
39 DEFAULT_ADDRESS_SCOPE = "noscope"
42 class RouterInfo(object):
50 self.ovs_driver = OVSInterfaceDriver(agent_conf)
51 self.router_id = router_id
52 self.ex_gw_port = None
53 self._snat_enabled = None
55 self.internal_ports = []
56 self.floating_ips = set()
57 # Invoke the setter for establishing initial SNAT action
59 self.use_ipv6 = use_ipv6
60 ns = namespaces.RouterNamespace(
61 router_id, agent_conf, interface_driver, use_ipv6, self.ovs_driver)
62 self.router_namespace = ns
63 self.ns_name = ns.name
64 self.available_mark_ids = set(range(ADDRESS_SCOPE_MARK_ID_MIN,
65 ADDRESS_SCOPE_MARK_ID_MAX))
66 self._address_scope_to_mark_id = {
67 DEFAULT_ADDRESS_SCOPE: self.available_mark_ids.pop()}
68 self.iptables_manager = iptables_manager.IptablesManager(
70 namespace=self.ns_name)
72 self.agent_conf = agent_conf
73 self.driver = interface_driver
74 # radvd is a neutron.agent.linux.ra.DaemonMonitor
77 def initialize(self, process_monitor):
78 """Initialize the router on the system.
80 This differs from __init__ in that this method actually affects the
81 system creating namespaces, starting processes, etc. The other merely
82 initializes the python object. This separates in-memory object
83 initialization from methods that actually go do stuff to the system.
85 :param process_monitor: The agent's process monitor instance.
87 self.process_monitor = process_monitor
88 self.radvd = ra.DaemonMonitor(self.router_id,
91 self.get_internal_device_name,
94 self.router_namespace.create()
101 def router(self, value):
105 # enable_snat by default if it wasn't specified by plugin
106 self._snat_enabled = self._router.get('enable_snat', True)
108 def get_internal_device_name(self, port_id):
109 return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
111 def get_external_device_name(self, port_id):
112 return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
114 def get_external_device_interface_name(self, ex_gw_port):
115 return self.get_external_device_name(ex_gw_port['id'])
117 def _update_routing_table(self, operation, route, namespace):
118 cmd = ['ip', 'route', operation, 'to', route['destination'],
119 'via', route['nexthop']]
120 ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
121 ip_wrapper.netns.execute(cmd, check_exit_code=False)
123 def update_routing_table(self, operation, route):
124 self._update_routing_table(operation, route, self.ns_name)
126 def routes_updated(self, old_routes, new_routes):
127 adds, removes = common_utils.diff_list_of_dict(old_routes,
130 LOG.debug("Added route entry is '%s'", route)
131 # remove replaced route from deleted route
132 for del_route in removes:
133 if route['destination'] == del_route['destination']:
134 removes.remove(del_route)
135 #replace success even if there is no existing route
136 self.update_routing_table('replace', route)
137 for route in removes:
138 LOG.debug("Removed route entry is '%s'", route)
139 self.update_routing_table('delete', route)
141 def get_ex_gw_port(self):
142 return self.router.get('gw_port')
144 def get_floating_ips(self):
145 """Filter Floating IPs to be hosted on this agent."""
146 return self.router.get(l3_constants.FLOATINGIP_KEY, [])
148 def floating_forward_rules(self, floating_ip, fixed_ip):
149 return [('PREROUTING', '-d %s/32 -j DNAT --to-destination %s' %
150 (floating_ip, fixed_ip)),
151 ('OUTPUT', '-d %s/32 -j DNAT --to-destination %s' %
152 (floating_ip, fixed_ip)),
153 ('float-snat', '-s %s/32 -j SNAT --to-source %s' %
154 (fixed_ip, floating_ip))]
156 def floating_mangle_rules(self, floating_ip, fixed_ip, internal_mark):
157 mark_traffic_to_floating_ip = (
158 'floatingip', '-d %s -j MARK --set-xmark %s' % (
159 floating_ip, internal_mark))
160 mark_traffic_from_fixed_ip = (
161 'FORWARD', '-s %s -j $float-snat' % fixed_ip)
162 return [mark_traffic_to_floating_ip, mark_traffic_from_fixed_ip]
164 def get_address_scope_mark_mask(self, address_scope=None):
165 if not address_scope:
166 address_scope = DEFAULT_ADDRESS_SCOPE
168 if address_scope not in self._address_scope_to_mark_id:
169 self._address_scope_to_mark_id[address_scope] = (
170 self.available_mark_ids.pop())
172 mark_id = self._address_scope_to_mark_id[address_scope]
173 # NOTE: Address scopes use only the upper 16 bits of the 32 fwmark
174 return "%s/%s" % (hex(mark_id << 16), ADDRESS_SCOPE_MARK_MASK)
176 def get_port_address_scope_mark(self, port):
177 """Get the IP version 4 and 6 address scope mark for the port
179 :param port: A port dict from the RPC call
180 :returns: A dict mapping the address family to the address scope mark
182 port_scopes = port.get('address_scopes', {})
184 address_scope_mark_masks = (
185 (int(k), self.get_address_scope_mark_mask(v))
186 for k, v in port_scopes.items())
187 return collections.defaultdict(self.get_address_scope_mark_mask,
188 address_scope_mark_masks)
190 def process_floating_ip_nat_rules(self):
191 """Configure NAT rules for the router's floating IPs.
193 Configures iptables rules for the floating ips of the given router
195 # Clear out all iptables rules for floating ips
196 self.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip')
198 floating_ips = self.get_floating_ips()
199 # Loop once to ensure that floating ips are configured.
200 for fip in floating_ips:
201 # Rebuild iptables rules for the floating ip.
202 fixed = fip['fixed_ip_address']
203 fip_ip = fip['floating_ip_address']
204 for chain, rule in self.floating_forward_rules(fip_ip, fixed):
205 self.iptables_manager.ipv4['nat'].add_rule(chain, rule,
208 self.iptables_manager.apply()
210 def process_floating_ip_address_scope_rules(self):
211 """Configure address scope related iptables rules for the router's
215 # Clear out all iptables rules for floating ips
216 self.iptables_manager.ipv4['mangle'].clear_rules_by_tag('floating_ip')
217 all_floating_ips = self.get_floating_ips()
218 ext_scope = self._get_external_address_scope()
219 # Filter out the floating ips that have fixed ip in the same address
220 # scope. Because the packets for them will always be in one address
221 # scope, no need to manipulate MARK/CONNMARK for them.
222 floating_ips = [fip for fip in all_floating_ips
223 if fip.get('fixed_ip_address_scope') != ext_scope]
225 ext_scope_mark = self.get_address_scope_mark_mask(ext_scope)
226 ports_scopemark = self._get_address_scope_mark()
227 devices_in_ext_scope = {
228 device for device, mark
229 in ports_scopemark[l3_constants.IP_VERSION_4].items()
230 if mark == ext_scope_mark}
231 # Add address scope for floatingip egress
232 for device in devices_in_ext_scope:
233 self.iptables_manager.ipv4['mangle'].add_rule(
235 '-o %s -j MARK --set-xmark %s'
236 % (device, ext_scope_mark),
239 # Loop once to ensure that floating ips are configured.
240 for fip in floating_ips:
241 # Rebuild iptables rules for the floating ip.
242 fip_ip = fip['floating_ip_address']
243 # Send the floating ip traffic to the right address scope
244 fixed_ip = fip['fixed_ip_address']
245 fixed_scope = fip.get('fixed_ip_address_scope')
246 internal_mark = self.get_address_scope_mark_mask(fixed_scope)
247 mangle_rules = self.floating_mangle_rules(
248 fip_ip, fixed_ip, internal_mark)
249 for chain, rule in mangle_rules:
250 self.iptables_manager.ipv4['mangle'].add_rule(
251 chain, rule, tag='floating_ip')
253 def process_snat_dnat_for_fip(self):
255 self.process_floating_ip_nat_rules()
257 # TODO(salv-orlando): Less broad catching
258 msg = _('L3 agent failure to setup NAT for floating IPs')
260 raise n_exc.FloatingIpSetupException(msg)
262 def _add_fip_addr_to_device(self, fip, device):
263 """Configures the floating ip address on the device.
266 ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address'])
267 device.addr.add(ip_cidr)
270 # any exception occurred here should cause the floating IP
271 # to be set in error state
272 LOG.warning(_LW("Unable to configure IP address for "
273 "floating IP: %s"), fip['id'])
275 def add_floating_ip(self, fip, interface_name, device):
276 raise NotImplementedError()
278 def remove_floating_ip(self, device, ip_cidr):
279 device.delete_addr_and_conntrack_state(ip_cidr)
281 def move_floating_ip(self, fip):
282 return l3_constants.FLOATINGIP_STATUS_ACTIVE
284 def remove_external_gateway_ip(self, device, ip_cidr):
285 device.delete_addr_and_conntrack_state(ip_cidr)
287 def get_router_cidrs(self, device):
288 return set([addr['cidr'] for addr in device.addr.list()])
290 def process_floating_ip_addresses(self, interface_name):
291 """Configure IP addresses on router's external gateway interface.
293 Ensures addresses for existing floating IPs and cleans up
294 those that should not longer be configured.
298 if interface_name is None:
299 LOG.debug('No Interface for floating IPs router: %s',
303 device = ip_lib.IPDevice(interface_name, namespace=self.ns_name)
304 existing_cidrs = self.get_router_cidrs(device)
307 floating_ips = self.get_floating_ips()
308 # Loop once to ensure that floating ips are configured.
309 for fip in floating_ips:
310 fip_ip = fip['floating_ip_address']
311 ip_cidr = common_utils.ip_to_cidr(fip_ip)
312 new_cidrs.add(ip_cidr)
313 fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ACTIVE
314 if ip_cidr not in existing_cidrs:
315 fip_statuses[fip['id']] = self.add_floating_ip(
316 fip, interface_name, device)
317 LOG.debug('Floating ip %(id)s added, status %(status)s',
319 'status': fip_statuses.get(fip['id'])})
320 elif (fip_ip in self.fip_map and
321 self.fip_map[fip_ip] != fip['fixed_ip_address']):
322 LOG.debug("Floating IP was moved from fixed IP "
323 "%(old)s to %(new)s",
324 {'old': self.fip_map[fip_ip],
325 'new': fip['fixed_ip_address']})
326 fip_statuses[fip['id']] = self.move_floating_ip(fip)
327 elif fip_statuses[fip['id']] == fip['status']:
328 # mark the status as not changed. we can't remove it because
329 # that's how the caller determines that it was removed
330 fip_statuses[fip['id']] = FLOATINGIP_STATUS_NOCHANGE
332 ip_cidr for ip_cidr in existing_cidrs - new_cidrs
333 if common_utils.is_cidr_host(ip_cidr))
334 for ip_cidr in fips_to_remove:
335 LOG.debug("Removing floating ip %s from interface %s in "
336 "namespace %s", ip_cidr, interface_name, self.ns_name)
337 self.remove_floating_ip(device, ip_cidr)
341 def configure_fip_addresses(self, interface_name):
343 return self.process_floating_ip_addresses(interface_name)
345 # TODO(salv-orlando): Less broad catching
346 msg = _('L3 agent failure to setup floating IPs')
348 raise n_exc.FloatingIpSetupException(msg)
350 def put_fips_in_error_state(self):
352 for fip in self.router.get(l3_constants.FLOATINGIP_KEY, []):
353 fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ERROR
356 def delete(self, agent):
357 self.router['gw_port'] = None
358 self.router[l3_constants.INTERFACE_KEY] = []
359 self.router[l3_constants.FLOATINGIP_KEY] = []
360 self.process_delete(agent)
362 self.router_namespace.delete()
364 def _internal_network_updated(self, port, subnet_id, prefix, old_prefix,
366 interface_name = self.get_internal_device_name(port['id'])
367 if prefix != l3_constants.PROVISIONAL_IPV6_PD_PREFIX:
368 fixed_ips = port['fixed_ips']
369 for fixed_ip in fixed_ips:
370 if fixed_ip['subnet_id'] == subnet_id:
371 v6addr = common_utils.ip_to_cidr(fixed_ip['ip_address'],
372 fixed_ip.get('prefixlen'))
373 if v6addr not in updated_cidrs:
374 self.driver.add_ipv6_addr(interface_name, v6addr,
377 self.driver.delete_ipv6_addr_with_prefix(interface_name,
381 def _internal_network_added(self, ns_name, network_id, port_id,
382 fixed_ips, mac_address,
383 interface_name, prefix, mtu=None):
384 LOG.debug("adding internal network: prefix(%s), port(%s)",
386 self.driver.plug(network_id, port_id, interface_name, mac_address,
388 prefix=prefix, mtu=mtu)
390 ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips)
391 self.driver.init_router_port(
392 interface_name, ip_cidrs, namespace=ns_name)
393 for fixed_ip in fixed_ips:
394 ip_lib.send_ip_addr_adv_notif(ns_name,
396 fixed_ip['ip_address'],
399 def internal_network_added(self, port):
400 network_id = port['network_id']
402 fixed_ips = port['fixed_ips']
403 mac_address = port['mac_address']
405 interface_name = self.get_internal_device_name(port_id)
407 self._internal_network_added(self.ns_name,
416 def internal_network_removed(self, port):
417 interface_name = self.get_internal_device_name(port['id'])
418 LOG.debug("removing internal network: port(%s) interface(%s)",
419 port['id'], interface_name)
420 if ip_lib.device_exists(interface_name, namespace=self.ns_name):
421 self.driver.unplug(interface_name, namespace=self.ns_name,
422 prefix=INTERNAL_DEV_PREFIX)
424 def _get_existing_devices(self):
425 ip_wrapper = ip_lib.IPWrapper(namespace=self.ns_name)
426 ip_devs = ip_wrapper.get_devices(exclude_loopback=True)
427 return [ip_dev.name for ip_dev in ip_devs]
430 def _get_updated_ports(existing_ports, current_ports):
431 updated_ports = dict()
432 current_ports_dict = {p['id']: p for p in current_ports}
433 for existing_port in existing_ports:
434 current_port = current_ports_dict.get(existing_port['id'])
436 if (sorted(existing_port['fixed_ips'],
437 key=common_utils.safe_sort_key) !=
438 sorted(current_port['fixed_ips'],
439 key=common_utils.safe_sort_key)):
440 updated_ports[current_port['id']] = current_port
444 def _port_has_ipv6_subnet(port):
445 if 'subnets' in port:
446 for subnet in port['subnets']:
447 if (netaddr.IPNetwork(subnet['cidr']).version == 6 and
448 subnet['cidr'] != l3_constants.PROVISIONAL_IPV6_PD_PREFIX):
451 def enable_radvd(self, internal_ports=None):
452 LOG.debug('Spawning radvd daemon in router device: %s', self.router_id)
453 if not internal_ports:
454 internal_ports = self.internal_ports
455 self.radvd.enable(internal_ports)
457 def disable_radvd(self):
458 LOG.debug('Terminating radvd daemon in router device: %s',
462 def internal_network_updated(self, interface_name, ip_cidrs):
463 self.driver.init_router_port(
466 namespace=self.ns_name)
468 def address_scope_mangle_rule(self, device_name, mark_mask):
469 return '-i %s -j MARK --set-xmark %s' % (device_name, mark_mask)
471 def address_scope_filter_rule(self, device_name, mark_mask):
472 return '-o %s -m mark ! --mark %s -j DROP' % (
473 device_name, mark_mask)
475 def _process_internal_ports(self, pd):
476 existing_port_ids = set(p['id'] for p in self.internal_ports)
478 internal_ports = self.router.get(l3_constants.INTERFACE_KEY, [])
479 current_port_ids = set(p['id'] for p in internal_ports
480 if p['admin_state_up'])
482 new_port_ids = current_port_ids - existing_port_ids
483 new_ports = [p for p in internal_ports if p['id'] in new_port_ids]
484 old_ports = [p for p in self.internal_ports
485 if p['id'] not in current_port_ids]
486 updated_ports = self._get_updated_ports(self.internal_ports,
491 self.internal_network_added(p)
492 LOG.debug("appending port %s to internal_ports cache", p)
493 self.internal_ports.append(p)
494 enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
495 for subnet in p['subnets']:
496 if ipv6_utils.is_ipv6_pd_enabled(subnet):
497 interface_name = self.get_internal_device_name(p['id'])
498 pd.enable_subnet(self.router_id, subnet['id'],
500 interface_name, p['mac_address'])
503 self.internal_network_removed(p)
504 LOG.debug("removing port %s from internal_ports cache", p)
505 self.internal_ports.remove(p)
506 enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
507 for subnet in p['subnets']:
508 if ipv6_utils.is_ipv6_pd_enabled(subnet):
509 pd.disable_subnet(self.router_id, subnet['id'])
513 for index, p in enumerate(internal_ports):
514 if not updated_ports.get(p['id']):
516 self.internal_ports[index] = updated_ports[p['id']]
517 interface_name = self.get_internal_device_name(p['id'])
518 ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips'])
519 LOG.debug("updating internal network for port %s", p)
520 updated_cidrs += ip_cidrs
521 self.internal_network_updated(interface_name, ip_cidrs)
522 enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
524 # Check if there is any pd prefix update
525 for p in internal_ports:
526 if p['id'] in (set(current_port_ids) & set(existing_port_ids)):
527 for subnet in p.get('subnets', []):
528 if ipv6_utils.is_ipv6_pd_enabled(subnet):
529 old_prefix = pd.update_subnet(self.router_id,
533 self._internal_network_updated(p, subnet['id'],
541 self.enable_radvd(internal_ports)
543 existing_devices = self._get_existing_devices()
544 current_internal_devs = set(n for n in existing_devices
545 if n.startswith(INTERNAL_DEV_PREFIX))
546 current_port_devs = set(self.get_internal_device_name(port_id)
547 for port_id in current_port_ids)
548 stale_devs = current_internal_devs - current_port_devs
549 for stale_dev in stale_devs:
550 LOG.debug('Deleting stale internal router device: %s',
552 pd.remove_stale_ri_ifname(self.router_id, stale_dev)
553 self.driver.unplug(stale_dev,
554 namespace=self.ns_name,
555 prefix=INTERNAL_DEV_PREFIX)
557 def _list_floating_ip_cidrs(self):
558 # Compute a list of addresses this router is supposed to have.
559 # This avoids unnecessarily removing those addresses and
560 # causing a momentarily network outage.
561 floating_ips = self.get_floating_ips()
562 return [common_utils.ip_to_cidr(ip['floating_ip_address'])
563 for ip in floating_ips]
565 def _plug_external_gateway(self, ex_gw_port, interface_name, ns_name):
566 self.ovs_driver.plug(ex_gw_port['network_id'],
569 ex_gw_port['mac_address'],
570 bridge=self.agent_conf.external_network_bridge,
572 prefix=EXTERNAL_DEV_PREFIX,
573 mtu=ex_gw_port.get('mtu'))
575 def _get_external_gw_ips(self, ex_gw_port):
577 if 'subnets' in ex_gw_port:
578 gateway_ips = [subnet['gateway_ip']
579 for subnet in ex_gw_port['subnets']
580 if subnet['gateway_ip']]
581 if self.use_ipv6 and not self.is_v6_gateway_set(gateway_ips):
582 # No IPv6 gateway is available, but IPv6 is enabled.
583 if self.agent_conf.ipv6_gateway:
584 # ipv6_gateway configured, use address for default route.
585 gateway_ips.append(self.agent_conf.ipv6_gateway)
588 def _add_route_to_gw(self, ex_gw_port, device_name,
589 namespace, preserve_ips):
590 # Note: ipv6_gateway is an ipv6 LLA
591 # and so doesn't need a special route
592 for subnet in ex_gw_port.get('subnets', []):
593 is_gateway_not_in_subnet = (subnet['gateway_ip'] and
594 not ipam_utils.check_subnet_ip(
596 subnet['gateway_ip']))
597 if is_gateway_not_in_subnet:
598 preserve_ips.append(subnet['gateway_ip'])
599 device = ip_lib.IPDevice(device_name, namespace=namespace)
600 device.route.add_route(subnet['gateway_ip'], scope='link')
602 def _external_gateway_added(self, ex_gw_port, interface_name,
603 ns_name, preserve_ips):
604 LOG.debug("External gateway added: port(%s), interface(%s), ns(%s)",
605 ex_gw_port, interface_name, ns_name)
606 self._plug_external_gateway(ex_gw_port, interface_name, ns_name)
608 # Build up the interface and gateway IP addresses that
609 # will be added to the interface.
610 ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
612 gateway_ips = self._get_external_gw_ips(ex_gw_port)
613 enable_ra_on_gw = False
614 if self.use_ipv6 and not self.is_v6_gateway_set(gateway_ips):
615 # There is no IPv6 gw_ip, use RouterAdvt for default route.
616 enable_ra_on_gw = True
618 self._add_route_to_gw(ex_gw_port, device_name=interface_name,
619 namespace=ns_name, preserve_ips=preserve_ips)
620 self.ovs_driver.init_router_port(
624 extra_subnets=ex_gw_port.get('extra_subnets', []),
625 preserve_ips=preserve_ips,
626 clean_connections=True)
628 device = ip_lib.IPDevice(interface_name, namespace=ns_name)
629 for ip in gateway_ips or []:
630 device.route.add_gateway(ip)
633 self.driver.configure_ipv6_ra(ns_name, interface_name)
635 for fixed_ip in ex_gw_port['fixed_ips']:
636 ip_lib.send_ip_addr_adv_notif(ns_name,
638 fixed_ip['ip_address'],
641 def is_v6_gateway_set(self, gateway_ips):
642 """Check to see if list of gateway_ips has an IPv6 gateway.
644 # Note - don't require a try-except here as all
645 # gateway_ips elements are valid addresses, if they exist.
646 return any(netaddr.IPAddress(gw_ip).version == 6
647 for gw_ip in gateway_ips)
649 def external_gateway_added(self, ex_gw_port, interface_name):
650 preserve_ips = self._list_floating_ip_cidrs()
651 self._external_gateway_added(
652 ex_gw_port, interface_name, self.ns_name, preserve_ips)
654 def external_gateway_updated(self, ex_gw_port, interface_name):
655 preserve_ips = self._list_floating_ip_cidrs()
656 self._external_gateway_added(
657 ex_gw_port, interface_name, self.ns_name, preserve_ips)
659 def external_gateway_removed(self, ex_gw_port, interface_name):
660 LOG.debug("External gateway removed: port(%s), interface(%s)",
661 ex_gw_port, interface_name)
662 device = ip_lib.IPDevice(interface_name, namespace=self.ns_name)
663 for ip_addr in ex_gw_port['fixed_ips']:
664 self.remove_external_gateway_ip(device,
665 common_utils.ip_to_cidr(
666 ip_addr['ip_address'],
667 ip_addr['prefixlen']))
668 self.ovs_driver.unplug(interface_name,
669 bridge=self.agent_conf.external_network_bridge,
670 namespace=self.ns_name,
671 prefix=EXTERNAL_DEV_PREFIX)
674 def _gateway_ports_equal(port1, port2):
675 return port1 == port2
677 def _process_external_gateway(self, ex_gw_port, pd):
678 # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port
679 ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or
680 self.ex_gw_port and self.ex_gw_port['id'])
682 interface_name = None
684 interface_name = self.get_external_device_name(ex_gw_port_id)
686 if not self.ex_gw_port:
687 self.external_gateway_added(ex_gw_port, interface_name)
688 pd.add_gw_interface(self.router['id'], interface_name)
689 elif not self._gateway_ports_equal(ex_gw_port, self.ex_gw_port):
690 self.external_gateway_updated(ex_gw_port, interface_name)
691 elif not ex_gw_port and self.ex_gw_port:
692 self.external_gateway_removed(self.ex_gw_port, interface_name)
693 pd.remove_gw_interface(self.router['id'])
695 existing_devices = self._get_existing_devices()
696 stale_devs = [dev for dev in existing_devices
697 if dev.startswith(EXTERNAL_DEV_PREFIX)
698 and dev != interface_name]
699 for stale_dev in stale_devs:
700 LOG.debug('Deleting stale external router device: %s', stale_dev)
701 pd.remove_gw_interface(self.router['id'])
702 self.ovs_driver.unplug(stale_dev,
703 bridge=self.agent_conf.external_network_bridge,
704 namespace=self.ns_name,
705 prefix=EXTERNAL_DEV_PREFIX)
707 # Process SNAT rules for external gateway
708 gw_port = self._router.get('gw_port')
709 self._handle_router_snat_rules(gw_port, interface_name)
711 def _prevent_snat_for_internal_traffic_rule(self, interface_name):
713 'POSTROUTING', '! -i %(interface_name)s '
714 '! -o %(interface_name)s -m conntrack ! '
715 '--ctstate DNAT -j ACCEPT' %
716 {'interface_name': interface_name})
718 def external_gateway_nat_fip_rules(self, ex_gw_ip, interface_name):
719 dont_snat_traffic_to_internal_ports_if_not_to_floating_ip = (
720 self._prevent_snat_for_internal_traffic_rule(interface_name))
721 # Makes replies come back through the router to reverse DNAT
722 ext_in_mark = self.agent_conf.external_ingress_mark
723 snat_internal_traffic_to_floating_ip = (
724 'snat', '-m mark ! --mark %s/%s '
725 '-m conntrack --ctstate DNAT '
726 '-j SNAT --to-source %s'
727 % (ext_in_mark, l3_constants.ROUTER_MARK_MASK, ex_gw_ip))
728 return [dont_snat_traffic_to_internal_ports_if_not_to_floating_ip,
729 snat_internal_traffic_to_floating_ip]
731 def external_gateway_nat_snat_rules(self, ex_gw_ip, interface_name):
732 snat_normal_external_traffic = (
733 'snat', '-o %s -j SNAT --to-source %s' %
734 (interface_name, ex_gw_ip))
735 return [snat_normal_external_traffic]
737 def external_gateway_mangle_rules(self, interface_name):
738 mark = self.agent_conf.external_ingress_mark
739 mark_packets_entering_external_gateway_port = (
740 'mark', '-i %s -j MARK --set-xmark %s/%s' %
741 (interface_name, mark, l3_constants.ROUTER_MARK_MASK))
742 return [mark_packets_entering_external_gateway_port]
744 def _empty_snat_chains(self, iptables_manager):
745 iptables_manager.ipv4['nat'].empty_chain('POSTROUTING')
746 iptables_manager.ipv4['nat'].empty_chain('snat')
747 iptables_manager.ipv4['mangle'].empty_chain('mark')
748 iptables_manager.ipv4['mangle'].empty_chain('POSTROUTING')
750 def _add_snat_rules(self, ex_gw_port, iptables_manager,
752 self.process_external_port_address_scope_routing(iptables_manager)
755 # ex_gw_port should not be None in this case
756 # NAT rules are added only if ex_gw_port has an IPv4 address
757 for ip_addr in ex_gw_port['fixed_ips']:
758 ex_gw_ip = ip_addr['ip_address']
759 if netaddr.IPAddress(ex_gw_ip).version == 4:
760 if self._snat_enabled:
761 rules = self.external_gateway_nat_snat_rules(
762 ex_gw_ip, interface_name)
764 iptables_manager.ipv4['nat'].add_rule(*rule)
766 rules = self.external_gateway_nat_fip_rules(
767 ex_gw_ip, interface_name)
769 iptables_manager.ipv4['nat'].add_rule(*rule)
770 rules = self.external_gateway_mangle_rules(interface_name)
772 iptables_manager.ipv4['mangle'].add_rule(*rule)
776 def _handle_router_snat_rules(self, ex_gw_port, interface_name):
777 self._empty_snat_chains(self.iptables_manager)
779 self.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat')
781 self._add_snat_rules(ex_gw_port,
782 self.iptables_manager,
785 def _process_external_on_delete(self, agent):
788 ex_gw_port = self.get_ex_gw_port()
789 self._process_external_gateway(ex_gw_port, agent.pd)
793 interface_name = self.get_external_device_interface_name(
795 fip_statuses = self.configure_fip_addresses(interface_name)
797 except (n_exc.FloatingIpSetupException):
798 # All floating IPs must be put in error state
799 LOG.exception(_LE("Failed to process floating IPs."))
800 fip_statuses = self.put_fips_in_error_state()
802 self.update_fip_statuses(agent, fip_statuses)
804 def process_external(self, agent):
807 with self.iptables_manager.defer_apply():
808 ex_gw_port = self.get_ex_gw_port()
809 self._process_external_gateway(ex_gw_port, agent.pd)
813 # Process SNAT/DNAT rules and addresses for floating IPs
814 self.process_snat_dnat_for_fip()
816 # Once NAT rules for floating IPs are safely in place
817 # configure their addresses on the external gateway port
818 interface_name = self.get_external_device_interface_name(
820 fip_statuses = self.configure_fip_addresses(interface_name)
822 except (n_exc.FloatingIpSetupException,
823 n_exc.IpTablesApplyException):
824 # All floating IPs must be put in error state
825 LOG.exception(_LE("Failed to process floating IPs."))
826 fip_statuses = self.put_fips_in_error_state()
828 self.update_fip_statuses(agent, fip_statuses)
830 def update_fip_statuses(self, agent, fip_statuses):
831 # Identify floating IPs which were disabled
832 existing_floating_ips = self.floating_ips
833 self.floating_ips = set(fip_statuses.keys())
834 for fip_id in existing_floating_ips - self.floating_ips:
835 fip_statuses[fip_id] = l3_constants.FLOATINGIP_STATUS_DOWN
836 # filter out statuses that didn't change
837 fip_statuses = {f: stat for f, stat in fip_statuses.items()
838 if stat != FLOATINGIP_STATUS_NOCHANGE}
841 LOG.debug('Sending floating ip statuses: %s', fip_statuses)
842 # Update floating IP status on the neutron server
843 agent.plugin_rpc.update_floatingip_statuses(
844 agent.context, self.router_id, fip_statuses)
846 def _get_port_devicename_scopemark(self, ports, name_generator):
847 devicename_scopemark = {l3_constants.IP_VERSION_4: dict(),
848 l3_constants.IP_VERSION_6: dict()}
850 device_name = name_generator(p['id'])
851 ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips'])
852 port_as_marks = self.get_port_address_scope_mark(p)
853 for ip_version in {ip_lib.get_ip_version(cidr)
854 for cidr in ip_cidrs}:
855 devicename_scopemark[ip_version][device_name] = (
856 port_as_marks[ip_version])
858 return devicename_scopemark
860 def _get_address_scope_mark(self):
861 # Prepare address scope iptables rule for internal ports
862 internal_ports = self.router.get(l3_constants.INTERFACE_KEY, [])
863 ports_scopemark = self._get_port_devicename_scopemark(
864 internal_ports, self.get_internal_device_name)
866 # Prepare address scope iptables rule for external port
867 external_port = self.get_ex_gw_port()
869 external_port_scopemark = self._get_port_devicename_scopemark(
870 [external_port], self.get_external_device_name)
871 for ip_version in (l3_constants.IP_VERSION_4,
872 l3_constants.IP_VERSION_6):
873 ports_scopemark[ip_version].update(
874 external_port_scopemark[ip_version])
875 return ports_scopemark
877 def _add_address_scope_mark(self, iptables_manager, ports_scopemark):
878 external_device_name = None
879 external_port = self.get_ex_gw_port()
881 external_device_name = self.get_external_device_name(
884 # Process address scope iptables rules
885 for ip_version in (l3_constants.IP_VERSION_4,
886 l3_constants.IP_VERSION_6):
887 scopemarks = ports_scopemark[ip_version]
888 iptables = iptables_manager.get_tables(ip_version)
889 iptables['mangle'].empty_chain('scope')
890 iptables['filter'].empty_chain('scope')
891 dont_block_external = (ip_version == l3_constants.IP_VERSION_4
892 and self._snat_enabled and external_port)
893 for device_name, mark in scopemarks.items():
894 # Add address scope iptables rule
895 iptables['mangle'].add_rule(
897 self.address_scope_mangle_rule(device_name, mark))
898 if dont_block_external and device_name == external_device_name:
900 iptables['filter'].add_rule(
902 self.address_scope_filter_rule(device_name, mark))
904 def process_ports_address_scope_iptables(self):
905 ports_scopemark = self._get_address_scope_mark()
906 self._add_address_scope_mark(self.iptables_manager, ports_scopemark)
908 def _get_external_address_scope(self):
909 external_port = self.get_ex_gw_port()
910 if not external_port:
913 scopes = external_port.get('address_scopes', {})
914 return scopes.get(str(l3_constants.IP_VERSION_4))
916 def process_external_port_address_scope_routing(self, iptables_manager):
917 if not self._snat_enabled:
920 external_port = self.get_ex_gw_port()
921 if not external_port:
924 external_devicename = self.get_external_device_name(
927 # Saves the originating address scope by saving the packet MARK to
928 # the CONNMARK for new connections so that returning traffic can be
930 rule = ('-o %s -m connmark --mark 0x0/0xffff0000 '
931 '-j CONNMARK --save-mark '
932 '--nfmask 0xffff0000 --ctmask 0xffff0000' %
935 iptables_manager.ipv4['mangle'].add_rule('POSTROUTING', rule)
937 address_scope = self._get_external_address_scope()
938 if not address_scope:
941 # Prevents snat within the same address scope
942 rule = '-o %s -m connmark --mark %s -j ACCEPT' % (
944 self.get_address_scope_mark_mask(address_scope))
945 iptables_manager.ipv4['nat'].add_rule('snat', rule)
947 def process_address_scope(self):
948 with self.iptables_manager.defer_apply():
949 self.process_ports_address_scope_iptables()
950 self.process_floating_ip_address_scope_rules()
952 @common_utils.exception_logger()
953 def process_delete(self, agent):
954 """Process the delete of this router
956 This method is the point where the agent requests that this router
957 be deleted. This is a separate code path from process in that it
958 avoids any changes to the qrouter namespace that will be removed
959 at the end of the operation.
961 :param agent: Passes the agent in order to send RPC messages.
963 LOG.debug("process router delete")
964 if self.router_namespace.exists():
965 self._process_internal_ports(agent.pd)
966 agent.pd.sync_router(self.router['id'])
967 self._process_external_on_delete(agent)
969 LOG.warning(_LW("Can't gracefully delete the router %s: "
970 "no router namespace found."), self.router['id'])
972 @common_utils.exception_logger()
973 def process(self, agent):
974 """Process updates to this router
976 This method is the point where the agent requests that updates be
977 applied to this router.
979 :param agent: Passes the agent in order to send RPC messages.
981 LOG.debug("process router updates")
982 self._process_internal_ports(agent.pd)
983 agent.pd.sync_router(self.router['id'])
984 self.process_external(agent)
985 self.process_address_scope()
986 # Process static routes for router
987 self.routes_updated(self.routes, self.router['routes'])
988 self.routes = self.router['routes']
990 # Update ex_gw_port and enable_snat on the router info cache
991 self.ex_gw_port = self.get_ex_gw_port()
992 self.fip_map = dict([(fip['floating_ip_address'],
993 fip['fixed_ip_address'])
994 for fip in self.get_floating_ips()])
995 # TODO(Carl) FWaaS uses this. Why is it set after processing is done?
996 self.enable_snat = self.router.get('enable_snat')