1 # Copyright (c) 2015 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
15 from oslo_log import log as logging
17 from neutron._i18n import _LE
18 from neutron.agent.l3 import dvr_local_router
19 from neutron.agent.l3 import dvr_snat_ns
20 from neutron.agent.l3 import router_info as router
21 from neutron.agent.linux import ip_lib
22 from neutron.agent.linux import iptables_manager
24 LOG = logging.getLogger(__name__)
27 class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
29 def __init__(self, agent, host, *args, **kwargs):
30 super(DvrEdgeRouter, self).__init__(agent, host, *args, **kwargs)
31 self.snat_namespace = None
32 self.snat_iptables_manager = None
34 def external_gateway_added(self, ex_gw_port, interface_name):
35 super(DvrEdgeRouter, self).external_gateway_added(
36 ex_gw_port, interface_name)
37 if self._is_this_snat_host():
38 self._create_dvr_gateway(ex_gw_port, interface_name)
39 # NOTE: When a router is created without a gateway the routes get
40 # added to the router namespace, but if we wanted to populate
41 # the same routes to the snat namespace after the gateway port
42 # is added, we need to call routes_updated here.
43 self.routes_updated([], self.router['routes'])
45 def external_gateway_updated(self, ex_gw_port, interface_name):
46 if not self._is_this_snat_host():
47 # no centralized SNAT gateway for this node/agent
48 LOG.debug("not hosting snat for router: %s", self.router['id'])
49 if self.snat_namespace:
50 LOG.debug("SNAT was rescheduled to host %s. Clearing snat "
51 "namespace.", self.router.get('gw_port_host'))
52 return self.external_gateway_removed(
53 ex_gw_port, interface_name)
56 if not self.snat_namespace:
57 # SNAT might be rescheduled to this agent; need to process like
58 # newly created gateway
59 return self.external_gateway_added(ex_gw_port, interface_name)
61 self._external_gateway_added(ex_gw_port,
63 self.snat_namespace.name,
66 def _external_gateway_removed(self, ex_gw_port, interface_name):
67 super(DvrEdgeRouter, self).external_gateway_removed(ex_gw_port,
69 if not self._is_this_snat_host() and not self.snat_namespace:
70 # no centralized SNAT gateway for this node/agent
71 LOG.debug("not hosting snat for router: %s", self.router['id'])
74 self.driver.unplug(interface_name,
75 bridge=self.agent_conf.external_network_bridge,
76 namespace=self.snat_namespace.name,
77 prefix=router.EXTERNAL_DEV_PREFIX)
79 def external_gateway_removed(self, ex_gw_port, interface_name):
80 self._external_gateway_removed(ex_gw_port, interface_name)
81 if self.snat_namespace:
82 self.snat_namespace.delete()
83 self.snat_namespace = None
85 def internal_network_added(self, port):
86 super(DvrEdgeRouter, self).internal_network_added(port)
88 # TODO(gsagie) some of this checks are already implemented
89 # in the base class, think how to avoid re-doing them
90 if not self._is_this_snat_host():
93 sn_port = self.get_snat_port_for_internal_port(port)
97 ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id'])
98 interface_name = self._get_snat_int_device_name(sn_port['id'])
99 self._internal_network_added(
101 sn_port['network_id'],
103 sn_port['fixed_ips'],
104 sn_port['mac_address'],
106 dvr_snat_ns.SNAT_INT_DEV_PREFIX)
108 def _dvr_internal_network_removed(self, port):
109 super(DvrEdgeRouter, self)._dvr_internal_network_removed(port)
111 if not self.ex_gw_port:
114 sn_port = self.get_snat_port_for_internal_port(port, self.snat_ports)
118 if not self._is_this_snat_host():
121 snat_interface = self._get_snat_int_device_name(sn_port['id'])
122 ns_name = self.snat_namespace.name
123 prefix = dvr_snat_ns.SNAT_INT_DEV_PREFIX
124 if ip_lib.device_exists(snat_interface, namespace=ns_name):
125 self.driver.unplug(snat_interface, namespace=ns_name,
128 def _plug_snat_port(self, port):
129 interface_name = self._get_snat_int_device_name(port['id'])
130 self._internal_network_added(
131 self.snat_namespace.name, port['network_id'],
132 port['id'], port['fixed_ips'],
133 port['mac_address'], interface_name,
134 dvr_snat_ns.SNAT_INT_DEV_PREFIX)
136 def _create_dvr_gateway(self, ex_gw_port, gw_interface_name):
137 """Create SNAT namespace."""
138 snat_ns = self._create_snat_namespace()
139 # connect snat_ports to br_int from SNAT namespace
140 for port in self.get_snat_interfaces():
141 # create interface_name
142 self._plug_snat_port(port)
143 self._external_gateway_added(ex_gw_port, gw_interface_name,
144 snat_ns.name, preserve_ips=[])
145 self.snat_iptables_manager = iptables_manager.IptablesManager(
146 namespace=snat_ns.name,
147 use_ipv6=self.use_ipv6)
148 # kicks the FW Agent to add rules for the snat namespace
149 self.agent.process_router_add(self)
151 def _create_snat_namespace(self):
152 # TODO(mlavalle): in the near future, this method should contain the
153 # code in the L3 agent that creates a gateway for a dvr. The first step
154 # is to move the creation of the snat namespace here
155 self.snat_namespace = dvr_snat_ns.SnatNamespace(self.router['id'],
159 self.snat_namespace.create()
160 return self.snat_namespace
162 def _get_snat_int_device_name(self, port_id):
163 long_name = dvr_snat_ns.SNAT_INT_DEV_PREFIX + port_id
164 return long_name[:self.driver.DEV_NAME_LEN]
166 def _is_this_snat_host(self):
167 host = self.router.get('gw_port_host')
169 LOG.debug("gw_port_host missing from router: %s",
171 return host == self.host
173 def _handle_router_snat_rules(self, ex_gw_port, interface_name):
174 if not self._is_this_snat_host():
176 if not self.get_ex_gw_port():
179 if not self.snat_iptables_manager:
180 LOG.debug("DVR router: no snat rules to be handled")
183 with self.snat_iptables_manager.defer_apply():
184 self._empty_snat_chains(self.snat_iptables_manager)
186 # NOTE DVR doesn't add the jump to float snat like the super class.
188 self._add_snat_rules(ex_gw_port, self.snat_iptables_manager,
191 def update_routing_table(self, operation, route):
192 if self.get_ex_gw_port() and self._is_this_snat_host():
193 ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
195 # NOTE: For now let us apply the static routes both in SNAT
196 # namespace and Router Namespace, to reduce the complexity.
197 ip_wrapper = ip_lib.IPWrapper(namespace=ns_name)
198 if ip_wrapper.netns.exists(ns_name):
199 super(DvrEdgeRouter, self)._update_routing_table(
200 operation, route, namespace=ns_name)
202 LOG.error(_LE("The SNAT namespace %s does not exist for "
203 "the router."), ns_name)
204 super(DvrEdgeRouter, self).update_routing_table(operation, route)
206 def delete(self, agent):
207 super(DvrEdgeRouter, self).delete(agent)
208 if self.snat_namespace:
209 self.snat_namespace.delete()