0ddd1db5e0d78b5db9658c56b34912e11a1b569f
[apex.git] / build / neutron / agent / l3 / router_info.py
1 # Copyright (c) 2014 OpenStack Foundation
2 #
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
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
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
13 #    under the License.
14
15 import collections
16 import netaddr
17 from oslo_log import log as logging
18
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
30
31 LOG = logging.getLogger(__name__)
32 INTERNAL_DEV_PREFIX = namespaces.INTERNAL_DEV_PREFIX
33 EXTERNAL_DEV_PREFIX = namespaces.EXTERNAL_DEV_PREFIX
34
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"
40
41
42 class RouterInfo(object):
43
44     def __init__(self,
45                  router_id,
46                  router,
47                  agent_conf,
48                  interface_driver,
49                  use_ipv6=False):
50         self.ovs_driver = OVSInterfaceDriver(agent_conf)
51         self.router_id = router_id
52         self.ex_gw_port = None
53         self._snat_enabled = None
54         self.fip_map = {}
55         self.internal_ports = []
56         self.floating_ips = set()
57         # Invoke the setter for establishing initial SNAT action
58         self.router = router
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(
69             use_ipv6=use_ipv6,
70             namespace=self.ns_name)
71         self.routes = []
72         self.agent_conf = agent_conf
73         self.driver = interface_driver
74         # radvd is a neutron.agent.linux.ra.DaemonMonitor
75         self.radvd = None
76
77     def initialize(self, process_monitor):
78         """Initialize the router on the system.
79
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.
84
85         :param process_monitor: The agent's process monitor instance.
86         """
87         self.process_monitor = process_monitor
88         self.radvd = ra.DaemonMonitor(self.router_id,
89                                       self.ns_name,
90                                       process_monitor,
91                                       self.get_internal_device_name,
92                                       self.agent_conf)
93
94         self.router_namespace.create()
95
96     @property
97     def router(self):
98         return self._router
99
100     @router.setter
101     def router(self, value):
102         self._router = value
103         if not self._router:
104             return
105         # enable_snat by default if it wasn't specified by plugin
106         self._snat_enabled = self._router.get('enable_snat', True)
107
108     def get_internal_device_name(self, port_id):
109         return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
110
111     def get_external_device_name(self, port_id):
112         return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
113
114     def get_external_device_interface_name(self, ex_gw_port):
115         return self.get_external_device_name(ex_gw_port['id'])
116
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)
122
123     def update_routing_table(self, operation, route):
124         self._update_routing_table(operation, route, self.ns_name)
125
126     def routes_updated(self, old_routes, new_routes):
127         adds, removes = common_utils.diff_list_of_dict(old_routes,
128                                                        new_routes)
129         for route in adds:
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)
140
141     def get_ex_gw_port(self):
142         return self.router.get('gw_port')
143
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, [])
147
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))]
155
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]
163
164     def get_address_scope_mark_mask(self, address_scope=None):
165         if not address_scope:
166             address_scope = DEFAULT_ADDRESS_SCOPE
167
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())
171
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)
175
176     def get_port_address_scope_mark(self, port):
177         """Get the IP version 4 and 6 address scope mark for the port
178
179         :param port: A port dict from the RPC call
180         :returns: A dict mapping the address family to the address scope mark
181         """
182         port_scopes = port.get('address_scopes', {})
183
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)
189
190     def process_floating_ip_nat_rules(self):
191         """Configure NAT rules for the router's floating IPs.
192
193         Configures iptables rules for the floating ips of the given router
194         """
195         # Clear out all iptables rules for floating ips
196         self.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip')
197
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,
206                                                            tag='floating_ip')
207
208         self.iptables_manager.apply()
209
210     def process_floating_ip_address_scope_rules(self):
211         """Configure address scope related iptables rules for the router's
212          floating IPs.
213         """
214
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]
224         if floating_ips:
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(
234                     'float-snat',
235                     '-o %s -j MARK --set-xmark %s'
236                     % (device, ext_scope_mark),
237                     tag='floating_ip')
238
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')
252
253     def process_snat_dnat_for_fip(self):
254         try:
255             self.process_floating_ip_nat_rules()
256         except Exception:
257             # TODO(salv-orlando): Less broad catching
258             msg = _('L3 agent failure to setup NAT for floating IPs')
259             LOG.exception(msg)
260             raise n_exc.FloatingIpSetupException(msg)
261
262     def _add_fip_addr_to_device(self, fip, device):
263         """Configures the floating ip address on the device.
264         """
265         try:
266             ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address'])
267             device.addr.add(ip_cidr)
268             return True
269         except RuntimeError:
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'])
274
275     def add_floating_ip(self, fip, interface_name, device):
276         raise NotImplementedError()
277
278     def remove_floating_ip(self, device, ip_cidr):
279         device.delete_addr_and_conntrack_state(ip_cidr)
280
281     def move_floating_ip(self, fip):
282         return l3_constants.FLOATINGIP_STATUS_ACTIVE
283
284     def remove_external_gateway_ip(self, device, ip_cidr):
285         device.delete_addr_and_conntrack_state(ip_cidr)
286
287     def get_router_cidrs(self, device):
288         return set([addr['cidr'] for addr in device.addr.list()])
289
290     def process_floating_ip_addresses(self, interface_name):
291         """Configure IP addresses on router's external gateway interface.
292
293         Ensures addresses for existing floating IPs and cleans up
294         those that should not longer be configured.
295         """
296
297         fip_statuses = {}
298         if interface_name is None:
299             LOG.debug('No Interface for floating IPs router: %s',
300                       self.router['id'])
301             return fip_statuses
302
303         device = ip_lib.IPDevice(interface_name, namespace=self.ns_name)
304         existing_cidrs = self.get_router_cidrs(device)
305         new_cidrs = set()
306
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',
318                           {'id': fip['id'],
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
331         fips_to_remove = (
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)
338
339         return fip_statuses
340
341     def configure_fip_addresses(self, interface_name):
342         try:
343             return self.process_floating_ip_addresses(interface_name)
344         except Exception:
345             # TODO(salv-orlando): Less broad catching
346             msg = _('L3 agent failure to setup floating IPs')
347             LOG.exception(msg)
348             raise n_exc.FloatingIpSetupException(msg)
349
350     def put_fips_in_error_state(self):
351         fip_statuses = {}
352         for fip in self.router.get(l3_constants.FLOATINGIP_KEY, []):
353             fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ERROR
354         return fip_statuses
355
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)
361         self.disable_radvd()
362         self.router_namespace.delete()
363
364     def _internal_network_updated(self, port, subnet_id, prefix, old_prefix,
365                                   updated_cidrs):
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,
375                                                   self.ns_name)
376         else:
377             self.driver.delete_ipv6_addr_with_prefix(interface_name,
378                                                      old_prefix,
379                                                      self.ns_name)
380
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)",
385                   prefix, port_id)
386         self.driver.plug(network_id, port_id, interface_name, mac_address,
387                          namespace=ns_name,
388                          prefix=prefix, mtu=mtu)
389
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,
395                                           interface_name,
396                                           fixed_ip['ip_address'],
397                                           self.agent_conf)
398
399     def internal_network_added(self, port):
400         network_id = port['network_id']
401         port_id = port['id']
402         fixed_ips = port['fixed_ips']
403         mac_address = port['mac_address']
404
405         interface_name = self.get_internal_device_name(port_id)
406
407         self._internal_network_added(self.ns_name,
408                                      network_id,
409                                      port_id,
410                                      fixed_ips,
411                                      mac_address,
412                                      interface_name,
413                                      INTERNAL_DEV_PREFIX,
414                                      mtu=port.get('mtu'))
415
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)
423
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]
428
429     @staticmethod
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'])
435             if current_port:
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
441         return updated_ports
442
443     @staticmethod
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):
449                     return True
450
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)
456
457     def disable_radvd(self):
458         LOG.debug('Terminating radvd daemon in router device: %s',
459                   self.router_id)
460         self.radvd.disable()
461
462     def internal_network_updated(self, interface_name, ip_cidrs):
463         self.driver.init_router_port(
464             interface_name,
465             ip_cidrs=ip_cidrs,
466             namespace=self.ns_name)
467
468     def address_scope_mangle_rule(self, device_name, mark_mask):
469         return '-i %s -j MARK --set-xmark %s' % (device_name, mark_mask)
470
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)
474
475     def _process_internal_ports(self, pd):
476         existing_port_ids = set(p['id'] for p in self.internal_ports)
477
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'])
481
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,
487                                                 internal_ports)
488
489         enable_ra = False
490         for p in new_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'],
499                                      subnet['cidr'],
500                                      interface_name, p['mac_address'])
501
502         for p in old_ports:
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'])
510
511         updated_cidrs = []
512         if updated_ports:
513             for index, p in enumerate(internal_ports):
514                 if not updated_ports.get(p['id']):
515                     continue
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)
523
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,
530                                                       subnet['id'],
531                                                       subnet['cidr'])
532                         if old_prefix:
533                             self._internal_network_updated(p, subnet['id'],
534                                                            subnet['cidr'],
535                                                            old_prefix,
536                                                            updated_cidrs)
537                             enable_ra = True
538
539         # Enable RA
540         if enable_ra:
541             self.enable_radvd(internal_ports)
542
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',
551                       stale_dev)
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)
556
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]
564
565     def _plug_external_gateway(self, ex_gw_port, interface_name, ns_name):
566         self.ovs_driver.plug(ex_gw_port['network_id'],
567                          ex_gw_port['id'],
568                          interface_name,
569                          ex_gw_port['mac_address'],
570                          bridge=self.agent_conf.external_network_bridge,
571                          namespace=ns_name,
572                          prefix=EXTERNAL_DEV_PREFIX,
573                          mtu=ex_gw_port.get('mtu'))
574
575     def _get_external_gw_ips(self, ex_gw_port):
576         gateway_ips = []
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)
586         return gateway_ips
587
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(
595                                                 subnet['cidr'],
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')
601
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)
607
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'])
611
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
617
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(
621             interface_name,
622             ip_cidrs,
623             namespace=ns_name,
624             extra_subnets=ex_gw_port.get('extra_subnets', []),
625             preserve_ips=preserve_ips,
626             clean_connections=True)
627
628         device = ip_lib.IPDevice(interface_name, namespace=ns_name)
629         for ip in gateway_ips or []:
630             device.route.add_gateway(ip)
631
632         if enable_ra_on_gw:
633             self.driver.configure_ipv6_ra(ns_name, interface_name)
634
635         for fixed_ip in ex_gw_port['fixed_ips']:
636             ip_lib.send_ip_addr_adv_notif(ns_name,
637                                           interface_name,
638                                           fixed_ip['ip_address'],
639                                           self.agent_conf)
640
641     def is_v6_gateway_set(self, gateway_ips):
642         """Check to see if list of gateway_ips has an IPv6 gateway.
643         """
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)
648
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)
653
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)
658
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)
672
673     @staticmethod
674     def _gateway_ports_equal(port1, port2):
675         return port1 == port2
676
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'])
681
682         interface_name = None
683         if ex_gw_port_id:
684             interface_name = self.get_external_device_name(ex_gw_port_id)
685         if ex_gw_port:
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'])
694
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)
706
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)
710
711     def _prevent_snat_for_internal_traffic_rule(self, interface_name):
712         return (
713             'POSTROUTING', '! -i %(interface_name)s '
714                            '! -o %(interface_name)s -m conntrack ! '
715                            '--ctstate DNAT -j ACCEPT' %
716                            {'interface_name': interface_name})
717
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]
730
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]
736
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]
743
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')
749
750     def _add_snat_rules(self, ex_gw_port, iptables_manager,
751                         interface_name):
752         self.process_external_port_address_scope_routing(iptables_manager)
753
754         if ex_gw_port:
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)
763                         for rule in rules:
764                             iptables_manager.ipv4['nat'].add_rule(*rule)
765
766                     rules = self.external_gateway_nat_fip_rules(
767                         ex_gw_ip, interface_name)
768                     for rule in rules:
769                         iptables_manager.ipv4['nat'].add_rule(*rule)
770                     rules = self.external_gateway_mangle_rules(interface_name)
771                     for rule in rules:
772                         iptables_manager.ipv4['mangle'].add_rule(*rule)
773
774                     break
775
776     def _handle_router_snat_rules(self, ex_gw_port, interface_name):
777         self._empty_snat_chains(self.iptables_manager)
778
779         self.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat')
780
781         self._add_snat_rules(ex_gw_port,
782                              self.iptables_manager,
783                              interface_name)
784
785     def _process_external_on_delete(self, agent):
786         fip_statuses = {}
787         try:
788             ex_gw_port = self.get_ex_gw_port()
789             self._process_external_gateway(ex_gw_port, agent.pd)
790             if not ex_gw_port:
791                 return
792
793             interface_name = self.get_external_device_interface_name(
794                 ex_gw_port)
795             fip_statuses = self.configure_fip_addresses(interface_name)
796
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()
801         finally:
802             self.update_fip_statuses(agent, fip_statuses)
803
804     def process_external(self, agent):
805         fip_statuses = {}
806         try:
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)
810                 if not ex_gw_port:
811                     return
812
813                 # Process SNAT/DNAT rules and addresses for floating IPs
814                 self.process_snat_dnat_for_fip()
815
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(
819                 ex_gw_port)
820             fip_statuses = self.configure_fip_addresses(interface_name)
821
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()
827         finally:
828             self.update_fip_statuses(agent, fip_statuses)
829
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}
839         if not fip_statuses:
840             return
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)
845
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()}
849         for p in ports:
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])
857
858         return devicename_scopemark
859
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)
865
866         # Prepare address scope iptables rule for external port
867         external_port = self.get_ex_gw_port()
868         if external_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
876
877     def _add_address_scope_mark(self, iptables_manager, ports_scopemark):
878         external_device_name = None
879         external_port = self.get_ex_gw_port()
880         if external_port:
881             external_device_name = self.get_external_device_name(
882                 external_port['id'])
883
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(
896                     'scope',
897                     self.address_scope_mangle_rule(device_name, mark))
898                 if dont_block_external and device_name == external_device_name:
899                     continue
900                 iptables['filter'].add_rule(
901                     'scope',
902                     self.address_scope_filter_rule(device_name, mark))
903
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)
907
908     def _get_external_address_scope(self):
909         external_port = self.get_ex_gw_port()
910         if not external_port:
911             return
912
913         scopes = external_port.get('address_scopes', {})
914         return scopes.get(str(l3_constants.IP_VERSION_4))
915
916     def process_external_port_address_scope_routing(self, iptables_manager):
917         if not self._snat_enabled:
918             return
919
920         external_port = self.get_ex_gw_port()
921         if not external_port:
922             return
923
924         external_devicename = self.get_external_device_name(
925             external_port['id'])
926
927         # Saves the originating address scope by saving the packet MARK to
928         # the CONNMARK for new connections so that returning traffic can be
929         # match to it.
930         rule = ('-o %s -m connmark --mark 0x0/0xffff0000 '
931                 '-j CONNMARK --save-mark '
932                 '--nfmask 0xffff0000 --ctmask 0xffff0000' %
933                 external_devicename)
934
935         iptables_manager.ipv4['mangle'].add_rule('POSTROUTING', rule)
936
937         address_scope = self._get_external_address_scope()
938         if not address_scope:
939             return
940
941         # Prevents snat within the same address scope
942         rule = '-o %s -m connmark --mark %s -j ACCEPT' % (
943             external_devicename,
944             self.get_address_scope_mark_mask(address_scope))
945         iptables_manager.ipv4['nat'].add_rule('snat', rule)
946
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()
951
952     @common_utils.exception_logger()
953     def process_delete(self, agent):
954         """Process the delete of this router
955
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.
960
961         :param agent: Passes the agent in order to send RPC messages.
962         """
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)
968         else:
969             LOG.warning(_LW("Can't gracefully delete the router %s: "
970                             "no router namespace found."), self.router['id'])
971
972     @common_utils.exception_logger()
973     def process(self, agent):
974         """Process updates to this router
975
976         This method is the point where the agent requests that updates be
977         applied to this router.
978
979         :param agent: Passes the agent in order to send RPC messages.
980         """
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']
989
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')