91 # please see the comment above |
95 # please see the comment above |
92 dname = (EXTERNAL_DEV_PREFIX + port_id)[:13] |
96 dname = (EXTERNAL_DEV_PREFIX + port_id)[:13] |
93 dname += '_0' |
97 dname += '_0' |
94 return dname.replace('-', '_') |
98 return dname.replace('-', '_') |
95 |
99 |
96 def routes_updated(self): |
100 def update_routing_table(self, operation, route): |
97 pass |
101 if operation == 'replace': |
98 |
102 operation = 'change' |
99 def _get_existing_devices(self): |
103 cmd = ['/usr/sbin/route', 'get', route['destination']] |
100 return net_lib.Datalink.show_link() |
104 try: |
101 |
105 utils.execute(cmd, log_fail_as_error=False) |
102 def internal_network_added(self, port): |
106 except: |
103 internal_dlname = self.get_internal_device_name(port['id']) |
107 operation = 'add' |
104 # driver just returns if datalink and IP interface already exists |
108 cmd = ['/usr/sbin/route', operation, route['destination'], |
105 self.driver.plug(port['tenant_id'], port['network_id'], port['id'], |
109 route['nexthop']] |
106 internal_dlname, port['mac_address'], |
110 utils.execute(cmd) |
107 vif_type=port.get('binding:vif_type')) |
|
108 fixed_ips = port['fixed_ips'] |
|
109 ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips) |
|
110 self.driver.init_l3(internal_dlname, ip_cidrs) |
|
111 for fixed_ip in fixed_ips: |
|
112 net_lib.send_ip_addr_adv_notif(internal_dlname, |
|
113 fixed_ip['ip_address'], |
|
114 self.agent_conf) |
|
115 |
|
116 port_subnet = port['subnets'][0]['cidr'] |
|
117 ipversion = netaddr.IPNetwork(port_subnet).version |
|
118 rules = [] |
|
119 # if metadata is enabled, then we need to redirect all the packets |
|
120 # arriving at 169.254.169.254:80 to neutron-metadata-proxy server |
|
121 # listening at self.agent_conf.metadata_port |
|
122 if self.agent_conf.enable_metadata_proxy and ipversion == 4: |
|
123 rules.append('pass in quick proto tcp to 169.254.169.254/32 ' |
|
124 'port 80 rdr-to 127.0.0.1 port %s label metadata_%s ' |
|
125 'reply-to %s' % (self.agent_conf.metadata_port, |
|
126 internal_dlname, internal_dlname)) |
|
127 |
|
128 # Since we support shared router model, we need to block the new |
|
129 # internal port from reaching other tenant's ports. However, if |
|
130 # allow_forwarding_between_networks is set, then we need to |
|
131 # allow forwarding of packets between same tenant's ports. |
|
132 block_tblname = 'block_%s' % internal_dlname |
|
133 rules.append('block in quick to <%s> label %s' % |
|
134 (block_tblname, block_tblname)) |
|
135 if self.agent_conf.allow_forwarding_between_networks: |
|
136 allow_tblname = 'allow_%s' % internal_dlname |
|
137 rules.append('pass in quick to <%s> reply-to %s label %s' % |
|
138 (allow_tblname, internal_dlname, allow_tblname)) |
|
139 |
|
140 # finally add all the rules in one shot |
|
141 self.pf.add_rules(rules, [internal_dlname, 'normal']) |
|
142 |
|
143 ex_gw_port = self.ex_gw_port |
|
144 if not ex_gw_port: |
|
145 return |
|
146 |
|
147 ex_gw_ip = ex_gw_port['subnets'][0]['gateway_ip'] |
|
148 if not ex_gw_ip: |
|
149 return |
|
150 |
|
151 if netaddr.IPAddress(ex_gw_ip).version != 4 or ipversion != 4: |
|
152 return |
|
153 |
|
154 # if the external gateway is already setup for the shared router, |
|
155 # then we need to add Policy Based Routing (PBR) for both inbound |
|
156 # and outbound for this internal network |
|
157 external_dlname = self.get_external_device_name(ex_gw_port['id']) |
|
158 label = 'pbr_%s' % internal_dlname |
|
159 pbr_rules = ['pass in quick to !%s route-to {(%s %s)} label %s_in' % |
|
160 (port_subnet, external_dlname, ex_gw_ip, label)] |
|
161 pbr_rules.append('pass out quick received-on %s reply-to %s@%s ' |
|
162 'label %s_out' % (external_dlname, ex_gw_ip, |
|
163 external_dlname, label)) |
|
164 |
|
165 self.pf.add_rules(pbr_rules, [internal_dlname, 'pbr']) |
|
166 if self._snat_enabled: |
|
167 ex_gw_port_ip = ex_gw_port['fixed_ips'][0]['ip_address'] |
|
168 label = 'snat_%s' % internal_dlname |
|
169 snat_rule = ('pass out quick from %s to any nat-to %s label %s ' |
|
170 'reply-to %s' % (port_subnet, ex_gw_port_ip, label, |
|
171 internal_dlname)) |
|
172 self.pf.add_rules([snat_rule], [external_dlname, internal_dlname]) |
|
173 |
|
174 def internal_network_removed(self, port): |
|
175 internal_dlname = self.get_internal_device_name(port['id']) |
|
176 # remove the anchor and tables associated with this internal port |
|
177 self.pf.remove_anchor_recursively([internal_dlname]) |
|
178 if self.ex_gw_port and self._snat_enabled: |
|
179 external_dlname = self.\ |
|
180 get_external_device_name(self.ex_gw_port['id']) |
|
181 self.pf.remove_anchor_recursively([external_dlname, |
|
182 internal_dlname]) |
|
183 if net_lib.Datalink.datalink_exists(internal_dlname): |
|
184 self.driver.fini_l3(internal_dlname) |
|
185 self.driver.unplug(internal_dlname) |
|
186 |
|
187 def _apply_common_rules(self, all_subnets, internal_ports): |
|
188 v4_subnets = [subnet for subnet in all_subnets |
|
189 if netaddr.IPNetwork(subnet).version == 4] |
|
190 if not v4_subnets: |
|
191 return |
|
192 |
|
193 # add rule for metadata and broadcast |
|
194 allsubnets_tblname = "all_v4_subnets" |
|
195 common_aname = "common" |
|
196 self.pf.replace_table_entry(allsubnets_tblname, v4_subnets, |
|
197 [common_aname]) |
|
198 rules = [] |
|
199 # don't forward broadcast packets out of the internal subnet |
|
200 rules.append('pass in quick from <%s> to 255.255.255.255 label ' |
|
201 '%s_bcast' % (allsubnets_tblname, common_aname)) |
|
202 self.pf.add_rules(rules, [common_aname]) |
|
203 |
|
204 def _pre_setup_pf_rules(self, new_ports, old_ports, internal_ports): |
|
205 """We are going to do some amount of book keeping (for later use) and |
|
206 also pre-setup PF skeleton rules ahead of time to improve PF setup |
|
207 time. |
|
208 """ |
|
209 |
|
210 # Process PF anchor rules for internal ports in bulk as this |
|
211 # significantly improves the PF setup time. Capture the anchor |
|
212 # rules that will be placed under _auto/neutron:l3:agent. |
|
213 new_anchor_rules = set() |
|
214 for p in new_ports: |
|
215 port_id = p['id'] |
|
216 tenant_id = p['tenant_id'] |
|
217 subnet = p['subnets'][0]['cidr'] |
|
218 internal_dlname = self.get_internal_device_name(port_id) |
|
219 anchor_rule = 'anchor "%s/*" on %s all' % (internal_dlname, |
|
220 internal_dlname) |
|
221 new_anchor_rules.add(anchor_rule) |
|
222 ipnet = netaddr.IPNetwork(subnet) |
|
223 if ipnet.version == 4: |
|
224 self.ipnet_gwportname[ipnet] = internal_dlname |
|
225 # Capture all the subnets across all tenants and subnets |
|
226 # per-tenant. We will setup PF tables for each internal network |
|
227 # ahead of time |
|
228 self.tenant_subnets['all_tenants'].add(subnet) |
|
229 if tenant_id not in self.tenant_subnets: |
|
230 self.tenant_subnets[tenant_id] = set() |
|
231 self.tenant_subnets[tenant_id].add(subnet) |
|
232 |
|
233 old_anchor_rules = set() |
|
234 for p in old_ports: |
|
235 port_id = p['id'] |
|
236 tenant_id = p['tenant_id'] |
|
237 subnet = p['subnets'][0]['cidr'] |
|
238 internal_dlname = self.get_internal_device_name(port_id) |
|
239 anchor_rule = 'anchor "%s/*" on %s all' % (internal_dlname, |
|
240 internal_dlname) |
|
241 old_anchor_rules.add(anchor_rule) |
|
242 ipnet = netaddr.IPNetwork(subnet) |
|
243 if ipnet.version == 4: |
|
244 self.ipnet_gwportname.pop(ipnet, None) |
|
245 self.tenant_subnets['all_tenants'].discard(subnet) |
|
246 if tenant_id in self.tenant_subnets: |
|
247 self.tenant_subnets[tenant_id].discard(subnet) |
|
248 |
|
249 existing_anchor_rules = set(self.pf.list_anchor_rules()) |
|
250 final_anchor_rules = ((existing_anchor_rules | new_anchor_rules) - |
|
251 old_anchor_rules) |
|
252 # add an anchor rule to capture rules common amongst all the |
|
253 # internal ports under 'common' anchor |
|
254 if internal_ports: |
|
255 final_anchor_rules.add('anchor "common" all') |
|
256 # add rule for metadata and broadcast for all tenant's networks |
|
257 self._apply_common_rules(self.tenant_subnets['all_tenants'], |
|
258 internal_ports) |
|
259 else: |
111 else: |
260 final_anchor_rules.discard('anchor "common" all') |
112 assert operation == 'delete' |
261 # Now that there are no internal ports, remove the common anchor |
113 cmd = ['/usr/sbin/route', 'delete', route['destination'], |
262 # that captures rules common amongst all the internal ports |
114 route['nexthop']] |
263 self.pf.remove_anchor_recursively(['common']) |
115 utils.execute(cmd) |
264 self.pf.add_rules(list(sorted(final_anchor_rules))) |
|
265 |
|
266 # Since we support shared router model, we need to block the new |
|
267 # internal port from reaching other tenant's ports. However, if |
|
268 # allow_forwarding_between_networks is set, then we need to |
|
269 # allow forwarding of packets between same tenant's ports |
|
270 block_subnets = set() |
|
271 allow_subnets = set() |
|
272 for p in internal_ports: |
|
273 subnet = p['subnets'][0]['cidr'] |
|
274 tenant_id = p['tenant_id'] |
|
275 if self.agent_conf.allow_forwarding_between_networks: |
|
276 allow_subnets = self.tenant_subnets[tenant_id] - set([subnet]) |
|
277 block_subnets = (self.tenant_subnets['all_tenants'] - |
|
278 self.tenant_subnets[tenant_id]) |
|
279 else: |
|
280 block_subnets = (self.tenant_subnets['all_tenants'] - |
|
281 set([subnet])) |
|
282 # add table entry in the global scope |
|
283 internal_dlname = self.get_internal_device_name(p['id']) |
|
284 block_tblname = "block_%s" % internal_dlname |
|
285 self.pf.replace_table_entry(block_tblname, list(block_subnets), |
|
286 [internal_dlname, 'normal']) |
|
287 if allow_subnets: |
|
288 allow_tblname = "allow_%s" % internal_dlname |
|
289 self.pf.replace_table_entry(allow_tblname, list(allow_subnets), |
|
290 [internal_dlname, 'normal']) |
|
291 |
|
292 def _process_internal_ports(self): |
|
293 existing_port_ids = set([p['id'] for p in self.internal_ports]) |
|
294 |
|
295 internal_ports = self.router.get(l3_constants.INTERFACE_KEY, []) |
|
296 current_port_ids = set([p['id'] for p in internal_ports |
|
297 if p['admin_state_up']]) |
|
298 |
|
299 new_port_ids = current_port_ids - existing_port_ids |
|
300 new_ports = [p for p in internal_ports if p['id'] in new_port_ids] |
|
301 old_ports = [p for p in self.internal_ports if |
|
302 p['id'] not in current_port_ids] |
|
303 # updated_ports = self._get_updated_ports(self.internal_ports, |
|
304 # internal_ports) |
|
305 |
|
306 if old_ports or new_ports: |
|
307 self._pre_setup_pf_rules(new_ports, old_ports, internal_ports) |
|
308 |
|
309 enable_ra = False |
|
310 for p in new_ports: |
|
311 self.internal_network_added(p) |
|
312 self.internal_ports.append(p) |
|
313 enable_ra = enable_ra or self._port_has_ipv6_subnet(p) |
|
314 |
|
315 for p in old_ports: |
|
316 self.internal_network_removed(p) |
|
317 self.internal_ports.remove(p) |
|
318 enable_ra = enable_ra or self._port_has_ipv6_subnet(p) |
|
319 |
|
320 # if updated_ports: |
|
321 # for index, p in enumerate(internal_ports): |
|
322 # if not updated_ports.get(p['id']): |
|
323 # continue |
|
324 # self.internal_ports[index] = updated_ports[p['id']] |
|
325 # interface_name = self.get_internal_device_name(p['id']) |
|
326 # ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips']) |
|
327 # self.driver.init_l3(interface_name, ip_cidrs=ip_cidrs, |
|
328 # namespace=self.ns_name) |
|
329 # enable_ra = enable_ra or self._port_has_ipv6_subnet(p) |
|
330 |
|
331 # Enable RA |
|
332 if enable_ra: |
|
333 self.radvd.enable(internal_ports) |
|
334 |
|
335 # remove any internal stale router interfaces (i.e., l3i.. VNICs) |
|
336 existing_devices = self._get_existing_devices() |
|
337 current_internal_devs = set(n for n in existing_devices |
|
338 if n.startswith(INTERNAL_DEV_PREFIX)) |
|
339 current_port_devs = set(self.get_internal_device_name(port_id) |
|
340 for port_id in current_port_ids) |
|
341 stale_devs = current_internal_devs - current_port_devs |
|
342 for stale_dev in stale_devs: |
|
343 LOG.debug(_('Deleting stale internal router device: %s'), |
|
344 stale_dev) |
|
345 self.driver.fini_l3(stale_dev) |
|
346 self.driver.unplug(stale_dev) |
|
347 |
116 |
348 def _add_floating_ip_rules(self, interface_name, fip, fip_statuses): |
117 def _add_floating_ip_rules(self, interface_name, fip, fip_statuses): |
349 fixed_ip = fip['fixed_ip_address'] |
118 fixed_ip = fip['fixed_ip_address'] |
350 fip_ip = fip['floating_ip_address'] |
119 fip_ip = fip['floating_ip_address'] |
351 for ipnet, gwportname in self.ipnet_gwportname.iteritems(): |
120 for ipnet, gwportname in self.ipnet_gwportname.iteritems(): |
430 # new rules for the new fixed_ip |
199 # new rules for the new fixed_ip |
431 self.pf.remove_anchor([interface_name, fip_ip]) |
200 self.pf.remove_anchor([interface_name, fip_ip]) |
432 if not self._add_floating_ip_rules(interface_name, fip, |
201 if not self._add_floating_ip_rules(interface_name, fip, |
433 fip_statuses): |
202 fip_statuses): |
434 continue |
203 continue |
435 fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ACTIVE |
204 elif fip_statuses[fip['id']] == fip['status']: |
|
205 # mark the status as not changed. we can't remove it |
|
206 # because that's how the caller determines that it was |
|
207 # removed (TODO(gmoodalb): check this) |
|
208 fip_statuses[fip['id']] = router.FLOATINGIP_STATUS_NOCHANGE |
|
209 |
436 LOG.debug("Floating ip %(id)s added, status %(status)s", |
210 LOG.debug("Floating ip %(id)s added, status %(status)s", |
437 {'id': fip['id'], 'status': fip_statuses.get(fip['id'])}) |
211 {'id': fip['id'], |
|
212 'status': fip_statuses.get(fip['id'])}) |
438 |
213 |
439 # Clean up addresses that no longer belong on the gateway interface and |
214 # Clean up addresses that no longer belong on the gateway interface and |
440 # remove the binat-to PF rule associated with them |
215 # remove the binat-to PF rule associated with them |
441 for ip_cidr in existing_cidrs - new_cidrs: |
216 for ip_cidr in existing_cidrs - new_cidrs: |
442 if ip_cidr.endswith(FLOATING_IP_CIDR_SUFFIX): |
217 if ip_cidr.endswith(FLOATING_IP_CIDR_SUFFIX): |
|
218 LOG.debug("Removing floating ip %s from interface %s", |
|
219 ip_cidr, ipintf) |
443 self.pf.remove_anchor([interface_name, ip_cidr.split('/')[0]]) |
220 self.pf.remove_anchor([interface_name, ip_cidr.split('/')[0]]) |
444 ipintf.delete_address(ip_cidr, addrcheck=False) |
221 ipintf.delete_address(ip_cidr, addrcheck=False) |
445 return fip_statuses |
222 return fip_statuses |
446 |
223 |
|
224 def delete(self, agent): |
|
225 self.router['gw_port'] = None |
|
226 self.router[l3_constants.INTERFACE_KEY] = [] |
|
227 self.router[l3_constants.FLOATINGIP_KEY] = [] |
|
228 self.process_delete(agent) |
|
229 self.disable_radvd() |
|
230 |
|
231 def internal_network_added(self, port): |
|
232 internal_dlname = self.get_internal_device_name(port['id']) |
|
233 LOG.debug("adding internal network: port(%s), interface(%s)", |
|
234 port['id'], internal_dlname) |
|
235 # driver just returns if datalink and IP interface already exists |
|
236 self.driver.plug(port['tenant_id'], port['network_id'], port['id'], |
|
237 internal_dlname, port['mac_address'], |
|
238 vif_type=port.get('binding:vif_type')) |
|
239 fixed_ips = port['fixed_ips'] |
|
240 ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips) |
|
241 self.driver.init_l3(internal_dlname, ip_cidrs) |
|
242 for fixed_ip in fixed_ips: |
|
243 net_lib.send_ip_addr_adv_notif(internal_dlname, |
|
244 fixed_ip['ip_address'], |
|
245 self.agent_conf) |
|
246 |
|
247 port_subnet = port['subnets'][0]['cidr'] |
|
248 ipversion = netaddr.IPNetwork(port_subnet).version |
|
249 rules = [] |
|
250 # if metadata is enabled, then we need to redirect all the packets |
|
251 # arriving at 169.254.169.254:80 to neutron-metadata-proxy server |
|
252 # listening at self.agent_conf.metadata_port |
|
253 if self.agent_conf.enable_metadata_proxy and ipversion == 4: |
|
254 rules.append('pass in quick proto tcp to 169.254.169.254/32 ' |
|
255 'port 80 rdr-to 127.0.0.1 port %s label metadata_%s' |
|
256 % (self.agent_conf.metadata_port, internal_dlname)) |
|
257 |
|
258 # Since we support shared router model, we need to block the new |
|
259 # internal port from reaching other tenant's ports. However, if |
|
260 # allow_forwarding_between_networks is set, then we need to |
|
261 # allow forwarding of packets between same tenant's ports. |
|
262 block_tblname = 'block_%s' % internal_dlname |
|
263 rules.append('block in quick to <%s> label %s' % |
|
264 (block_tblname, block_tblname)) |
|
265 if self.agent_conf.allow_forwarding_between_networks: |
|
266 allow_tblname = 'allow_%s' % internal_dlname |
|
267 rules.append('pass in quick to <%s> reply-to %s label %s' % |
|
268 (allow_tblname, internal_dlname, allow_tblname)) |
|
269 |
|
270 # finally add all the rules in one shot |
|
271 self.pf.add_rules(rules, [internal_dlname, 'normal']) |
|
272 |
|
273 ex_gw_port = self.ex_gw_port |
|
274 if not ex_gw_port: |
|
275 return |
|
276 |
|
277 ex_gw_ip = ex_gw_port['subnets'][0]['gateway_ip'] |
|
278 if not ex_gw_ip: |
|
279 return |
|
280 |
|
281 if netaddr.IPAddress(ex_gw_ip).version != 4 or ipversion != 4: |
|
282 return |
|
283 |
|
284 # if the external gateway is already setup for the shared router, |
|
285 # then we need to add Policy Based Routing (PBR) for both inbound |
|
286 # and outbound for this internal network |
|
287 external_dlname = self.get_external_device_name(ex_gw_port['id']) |
|
288 label = 'pbr_%s' % internal_dlname |
|
289 pbr_rules = ['pass in quick to !%s route-to {(%s %s)} label %s_in' % |
|
290 (port_subnet, external_dlname, ex_gw_ip, label)] |
|
291 pbr_rules.append('pass out quick received-on %s reply-to %s@%s ' |
|
292 'label %s_out' % (external_dlname, ex_gw_ip, |
|
293 external_dlname, label)) |
|
294 |
|
295 self.pf.add_rules(pbr_rules, [internal_dlname, 'pbr']) |
|
296 if self._snat_enabled: |
|
297 ex_gw_port_ip = ex_gw_port['fixed_ips'][0]['ip_address'] |
|
298 label = 'snat_%s' % internal_dlname |
|
299 snat_rule = ('pass out quick from %s to any nat-to %s label %s ' |
|
300 'reply-to %s' % (port_subnet, ex_gw_port_ip, label, |
|
301 internal_dlname)) |
|
302 self.pf.add_rules([snat_rule], [external_dlname, internal_dlname]) |
|
303 |
|
304 def internal_network_removed(self, port): |
|
305 internal_dlname = self.get_internal_device_name(port['id']) |
|
306 LOG.debug("removing internal network: port(%s) interface(%s)", |
|
307 port['id'], internal_dlname) |
|
308 # remove the anchor and tables associated with this internal port |
|
309 self.pf.remove_anchor_recursively([internal_dlname]) |
|
310 if self.ex_gw_port and self._snat_enabled: |
|
311 external_dlname = self.\ |
|
312 get_external_device_name(self.ex_gw_port['id']) |
|
313 self.pf.remove_anchor_recursively([external_dlname, |
|
314 internal_dlname]) |
|
315 if net_lib.Datalink.datalink_exists(internal_dlname): |
|
316 self.driver.unplug(internal_dlname) |
|
317 |
|
318 def _get_existing_devices(self): |
|
319 return net_lib.Datalink.show_link() |
|
320 |
|
321 def internal_network_updated(self, interface_name, ip_cidrs): |
|
322 pass |
|
323 |
|
324 def _apply_common_rules(self, all_subnets, internal_ports): |
|
325 v4_subnets = [subnet for subnet in all_subnets |
|
326 if netaddr.IPNetwork(subnet).version == 4] |
|
327 if not v4_subnets: |
|
328 return |
|
329 |
|
330 # add rule for metadata and broadcast |
|
331 allsubnets_tblname = "all_v4_subnets" |
|
332 common_aname = "common" |
|
333 self.pf.replace_table_entry(allsubnets_tblname, v4_subnets, |
|
334 [common_aname]) |
|
335 rules = [] |
|
336 # don't forward broadcast packets out of the internal subnet |
|
337 rules.append('pass in quick from <%s> to 255.255.255.255 label ' |
|
338 '%s_bcast' % (allsubnets_tblname, common_aname)) |
|
339 self.pf.add_rules(rules, [common_aname]) |
|
340 |
|
341 def _pre_setup_pf_rules(self, new_ports, old_ports, internal_ports): |
|
342 """We are going to do some amount of book keeping (for later use) and |
|
343 also pre-setup PF skeleton rules ahead of time to improve PF setup |
|
344 time. |
|
345 """ |
|
346 |
|
347 # Process PF anchor rules for internal ports in bulk as this |
|
348 # significantly improves the PF setup time. Capture the anchor |
|
349 # rules that will be placed under _auto/neutron:l3:agent. |
|
350 new_anchor_rules = set() |
|
351 for p in new_ports: |
|
352 port_id = p['id'] |
|
353 tenant_id = p['tenant_id'] |
|
354 subnet = p['subnets'][0]['cidr'] |
|
355 internal_dlname = self.get_internal_device_name(port_id) |
|
356 anchor_rule = 'anchor "%s/*" on %s all' % (internal_dlname, |
|
357 internal_dlname) |
|
358 new_anchor_rules.add(anchor_rule) |
|
359 ipnet = netaddr.IPNetwork(subnet) |
|
360 if ipnet.version == 4: |
|
361 self.ipnet_gwportname[ipnet] = internal_dlname |
|
362 # Capture all the subnets across all tenants and subnets |
|
363 # per-tenant. We will setup PF tables for each internal network |
|
364 # ahead of time |
|
365 self.tenant_subnets['all_tenants'].add(subnet) |
|
366 if tenant_id not in self.tenant_subnets: |
|
367 self.tenant_subnets[tenant_id] = set() |
|
368 self.tenant_subnets[tenant_id].add(subnet) |
|
369 |
|
370 old_anchor_rules = set() |
|
371 for p in old_ports: |
|
372 port_id = p['id'] |
|
373 tenant_id = p['tenant_id'] |
|
374 subnet = p['subnets'][0]['cidr'] |
|
375 internal_dlname = self.get_internal_device_name(port_id) |
|
376 anchor_rule = 'anchor "%s/*" on %s all' % (internal_dlname, |
|
377 internal_dlname) |
|
378 old_anchor_rules.add(anchor_rule) |
|
379 ipnet = netaddr.IPNetwork(subnet) |
|
380 if ipnet.version == 4: |
|
381 self.ipnet_gwportname.pop(ipnet, None) |
|
382 self.tenant_subnets['all_tenants'].discard(subnet) |
|
383 if tenant_id in self.tenant_subnets: |
|
384 self.tenant_subnets[tenant_id].discard(subnet) |
|
385 |
|
386 existing_anchor_rules = set(self.pf.list_anchor_rules()) |
|
387 final_anchor_rules = ((existing_anchor_rules | new_anchor_rules) - |
|
388 old_anchor_rules) |
|
389 # add an anchor rule to capture rules common amongst all the |
|
390 # internal ports under 'common' anchor |
|
391 if internal_ports: |
|
392 final_anchor_rules.add('anchor "common" all') |
|
393 # add rule for metadata and broadcast for all tenant's networks |
|
394 self._apply_common_rules(self.tenant_subnets['all_tenants'], |
|
395 internal_ports) |
|
396 else: |
|
397 final_anchor_rules.discard('anchor "common" all') |
|
398 # Now that there are no internal ports, remove the common anchor |
|
399 # that captures rules common amongst all the internal ports |
|
400 self.pf.remove_anchor_recursively(['common']) |
|
401 self.pf.add_rules(list(sorted(final_anchor_rules))) |
|
402 |
|
403 # Since we support shared router model, we need to block the new |
|
404 # internal port from reaching other tenant's ports. However, if |
|
405 # allow_forwarding_between_networks is set, then we need to |
|
406 # allow forwarding of packets between same tenant's ports |
|
407 block_subnets = set() |
|
408 allow_subnets = set() |
|
409 for p in internal_ports: |
|
410 subnet = p['subnets'][0]['cidr'] |
|
411 tenant_id = p['tenant_id'] |
|
412 if self.agent_conf.allow_forwarding_between_networks: |
|
413 allow_subnets = self.tenant_subnets[tenant_id] - set([subnet]) |
|
414 block_subnets = (self.tenant_subnets['all_tenants'] - |
|
415 self.tenant_subnets[tenant_id]) |
|
416 else: |
|
417 block_subnets = (self.tenant_subnets['all_tenants'] - |
|
418 set([subnet])) |
|
419 # add table entry in the global scope |
|
420 internal_dlname = self.get_internal_device_name(p['id']) |
|
421 block_tblname = "block_%s" % internal_dlname |
|
422 self.pf.replace_table_entry(block_tblname, list(block_subnets), |
|
423 [internal_dlname, 'normal']) |
|
424 if allow_subnets: |
|
425 allow_tblname = "allow_%s" % internal_dlname |
|
426 self.pf.replace_table_entry(allow_tblname, list(allow_subnets), |
|
427 [internal_dlname, 'normal']) |
|
428 |
|
429 def _process_internal_ports(self, pd): |
|
430 existing_port_ids = set([p['id'] for p in self.internal_ports]) |
|
431 |
|
432 internal_ports = self.router.get(l3_constants.INTERFACE_KEY, []) |
|
433 current_port_ids = set([p['id'] for p in internal_ports |
|
434 if p['admin_state_up']]) |
|
435 |
|
436 new_port_ids = current_port_ids - existing_port_ids |
|
437 new_ports = [p for p in internal_ports if p['id'] in new_port_ids] |
|
438 old_ports = [p for p in self.internal_ports if |
|
439 p['id'] not in current_port_ids] |
|
440 # updated_ports = self._get_updated_ports(self.internal_ports, |
|
441 # internal_ports) |
|
442 |
|
443 if old_ports or new_ports: |
|
444 self._pre_setup_pf_rules(new_ports, old_ports, internal_ports) |
|
445 |
|
446 enable_ra = False |
|
447 for p in new_ports: |
|
448 self.internal_network_added(p) |
|
449 LOG.debug("appending port %s to internal_ports cache", p) |
|
450 self.internal_ports.append(p) |
|
451 enable_ra = enable_ra or self._port_has_ipv6_subnet(p) |
|
452 for subnet in p['subnets']: |
|
453 if ipv6_utils.is_ipv6_pd_enabled(subnet): |
|
454 interface_name = self.get_internal_device_name(p['id']) |
|
455 pd.enable_subnet(self.router_id, subnet['id'], |
|
456 subnet['cidr'], |
|
457 interface_name, p['mac_address']) |
|
458 |
|
459 for p in old_ports: |
|
460 self.internal_network_removed(p) |
|
461 LOG.debug("removing port %s from internal_ports cache", p) |
|
462 self.internal_ports.remove(p) |
|
463 enable_ra = enable_ra or self._port_has_ipv6_subnet(p) |
|
464 for subnet in p['subnets']: |
|
465 if ipv6_utils.is_ipv6_pd_enabled(subnet): |
|
466 pd.disable_subnet(self.router_id, subnet['id']) |
|
467 |
|
468 # updated_cidres = [] |
|
469 # if updated_ports: |
|
470 # for index, p in enumerate(internal_ports): |
|
471 # if not updated_ports.get(p['id']): |
|
472 # continue |
|
473 # self.internal_ports[index] = updated_ports[p['id']] |
|
474 # interface_name = self.get_internal_device_name(p['id']) |
|
475 # ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips']) |
|
476 # LOG.debug("updating internal network for port %s", p) |
|
477 # updated_cidrs += ip_cidrs |
|
478 |
|
479 # self.driver.init_l3(interface_name, ip_cidrs=ip_cidrs, |
|
480 # namespace=self.ns_name) |
|
481 # enable_ra = enable_ra or self._port_has_ipv6_subnet(p) |
|
482 |
|
483 # # Check if there is any pd prefix update |
|
484 # for p in internal_ports: |
|
485 # if p['id'] in (set(current_port_ids) & set(existing_port_ids)): |
|
486 # for subnet in p.get('subnets', []): |
|
487 # if ipv6_utils.is_ipv6_pd_enabled(subnet): |
|
488 # old_prefix = pd.update_subnet(self.router_id, |
|
489 # subnet['id'], |
|
490 # subnet['cidr']) |
|
491 # if old_prefix: |
|
492 # self._internal_network_updated(p, subnet['id'], |
|
493 # subnet['cidr'], |
|
494 # old_prefix, |
|
495 # updated_cidrs) |
|
496 # enable_ra = True |
|
497 # Enable RA |
|
498 if enable_ra: |
|
499 self.radvd.enable(internal_ports) |
|
500 |
|
501 # remove any internal stale router interfaces (i.e., l3i.. VNICs) |
|
502 existing_devices = self._get_existing_devices() |
|
503 current_internal_devs = set(n for n in existing_devices |
|
504 if n.startswith(INTERNAL_DEV_PREFIX)) |
|
505 current_port_devs = set(self.get_internal_device_name(port_id) |
|
506 for port_id in current_port_ids) |
|
507 stale_devs = current_internal_devs - current_port_devs |
|
508 for stale_dev in stale_devs: |
|
509 LOG.debug(_('Deleting stale internal router device: %s'), |
|
510 stale_dev) |
|
511 pd.remove_stale_ri_ifname(self.router_id, stale_dev) |
|
512 self.driver.unplug(stale_dev) |
|
513 |
447 # TODO(gmoodalb): need to do more work on ipv6 gateway |
514 # TODO(gmoodalb): need to do more work on ipv6 gateway |
448 def external_gateway_added(self, ex_gw_port, external_dlname): |
515 def external_gateway_added(self, ex_gw_port, external_dlname): |
|
516 LOG.debug("External gateway added: port(%s), interface(%s)", |
|
517 ex_gw_port, external_dlname) |
|
518 # TODO(gmoodalb): add MTU to plug()? |
449 self.driver.plug(ex_gw_port['tenant_id'], ex_gw_port['network_id'], |
519 self.driver.plug(ex_gw_port['tenant_id'], ex_gw_port['network_id'], |
450 ex_gw_port['id'], external_dlname, |
520 ex_gw_port['id'], external_dlname, |
451 ex_gw_port['mac_address'], |
521 ex_gw_port['mac_address'], |
452 bridge=self.agent_conf.external_network_bridge, |
522 bridge=self.agent_conf.external_network_bridge, |
453 vif_type=ex_gw_port.get('binding:vif_type')) |
523 vif_type=ex_gw_port.get('binding:vif_type')) |
507 cmd = ['/usr/bin/pfexec', '/usr/sbin/route', 'delete', |
579 cmd = ['/usr/bin/pfexec', '/usr/sbin/route', 'delete', |
508 'default', gw_ip] |
580 'default', gw_ip] |
509 utils.execute(cmd, check_exit_code=False) |
581 utils.execute(cmd, check_exit_code=False) |
510 |
582 |
511 if net_lib.Datalink.datalink_exists(external_dlname): |
583 if net_lib.Datalink.datalink_exists(external_dlname): |
512 self.driver.fini_l3(external_dlname) |
|
513 self.driver.unplug(external_dlname, |
584 self.driver.unplug(external_dlname, |
514 self.agent_conf.external_network_bridge) |
585 self.agent_conf.external_network_bridge) |
515 |
586 |
516 def _process_external_gateway(self, ex_gw_port): |
587 def _process_external_gateway(self, ex_gw_port, pd): |
517 # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port |
588 # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port |
518 ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or |
589 ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or |
519 self.ex_gw_port and self.ex_gw_port['id']) |
590 self.ex_gw_port and self.ex_gw_port['id']) |
520 |
591 |
521 ex_gw_port_status = None |
592 ex_gw_port_status = None |
522 interface_name = None |
593 interface_name = None |
523 if ex_gw_port_id: |
594 if ex_gw_port_id: |
524 interface_name = self.get_external_device_name(ex_gw_port_id) |
595 interface_name = self.get_external_device_name(ex_gw_port_id) |
525 if ex_gw_port: |
596 if ex_gw_port: |
526 def _gateway_ports_equal(port1, port2): |
|
527 def _get_filtered_dict(d, ignore): |
|
528 return dict((k, v) for k, v in d.iteritems() |
|
529 if k not in ignore) |
|
530 |
|
531 keys_to_ignore = set(['binding:host_id']) |
|
532 port1_filtered = _get_filtered_dict(port1, keys_to_ignore) |
|
533 port2_filtered = _get_filtered_dict(port2, keys_to_ignore) |
|
534 return port1_filtered == port2_filtered |
|
535 |
|
536 if not self.ex_gw_port: |
597 if not self.ex_gw_port: |
537 self.external_gateway_added(ex_gw_port, interface_name) |
598 self.external_gateway_added(ex_gw_port, interface_name) |
|
599 pd.add_gw_interface(self.router['id'], interface_name) |
538 ex_gw_port_status = 'added' |
600 ex_gw_port_status = 'added' |
539 elif not _gateway_ports_equal(ex_gw_port, self.ex_gw_port): |
601 elif not self._gateway_ports_equal(ex_gw_port, self.ex_gw_port): |
540 self.external_gateway_updated(ex_gw_port, interface_name) |
602 self.external_gateway_updated(ex_gw_port, interface_name) |
541 ex_gw_port_status = 'updated' |
603 ex_gw_port_status = 'updated' |
542 elif not ex_gw_port and self.ex_gw_port: |
604 elif not ex_gw_port and self.ex_gw_port: |
543 self.external_gateway_removed(self.ex_gw_port, interface_name) |
605 self.external_gateway_removed(self.ex_gw_port, interface_name) |
|
606 pd.remove_gw_interface(self.router['id']) |
544 ex_gw_port_status = 'removed' |
607 ex_gw_port_status = 'removed' |
545 |
608 |
546 # Remove any external stale router interfaces (i.e., l3e.. VNICs) |
609 # Remove any external stale router interfaces (i.e., l3e.. VNICs) |
547 existing_devices = self._get_existing_devices() |
610 existing_devices = self._get_existing_devices() |
548 stale_devs = [dev for dev in existing_devices |
611 stale_devs = [dev for dev in existing_devices |
549 if dev.startswith(EXTERNAL_DEV_PREFIX) and |
612 if dev.startswith(EXTERNAL_DEV_PREFIX) and |
550 dev != interface_name] |
613 dev != interface_name] |
551 for stale_dev in stale_devs: |
614 for stale_dev in stale_devs: |
552 LOG.debug(_('Deleting stale external router device: %s'), |
615 LOG.debug(_('Deleting stale external router device: %s'), |
553 stale_dev) |
616 stale_dev) |
554 self.driver.fini_l3(stale_dev) |
|
555 self.driver.unplug(stale_dev) |
617 self.driver.unplug(stale_dev) |
556 |
618 |
557 # Process SNAT rules for external gateway |
619 # Process SNAT rules for external gateway |
558 self.perform_snat_action(self._handle_router_snat_rules, |
620 gw_port = self._router.get('gw_port') |
559 interface_name, ex_gw_port_status) |
621 self._handle_router_snat_rules(gw_port, interface_name, |
|
622 ex_gw_port_status) |
560 |
623 |
561 def external_gateway_snat_rules(self, ex_gw_port_ip, external_dlname): |
624 def external_gateway_snat_rules(self, ex_gw_port_ip, external_dlname): |
562 rules = {} |
625 rules = {} |
563 for port in self.internal_ports: |
626 for port in self.internal_ports: |
564 ip_cidr = port['subnets'][0]['cidr'] |
627 ip_cidr = port['subnets'][0]['cidr'] |
583 for snat_anchor in snat_anchors: |
649 for snat_anchor in snat_anchors: |
584 if "/l3i" in snat_anchor: |
650 if "/l3i" in snat_anchor: |
585 self.pf.remove_anchor(snat_anchor.split('/')[-2:]) |
651 self.pf.remove_anchor(snat_anchor.split('/')[-2:]) |
586 |
652 |
587 # And add them back if the action is add_rules |
653 # And add them back if the action is add_rules |
588 if action == 'add_rules' and ex_gw_port_status in ['added', 'updated']: |
654 if ex_gw_port_status in ['added', 'updated']: |
589 # NAT rules are added only if ex_gw_port has an IPv4 address |
655 # NAT rules are added only if ex_gw_port has an IPv4 address |
590 ex_gw_port_ip = ex_gw_port['fixed_ips'][0]['ip_address'] |
656 ex_gw_port_ip = ex_gw_port['fixed_ips'][0]['ip_address'] |
591 if netaddr.IPAddress(ex_gw_port_ip).version != 4: |
657 if netaddr.IPAddress(ex_gw_port_ip).version != 4: |
592 return |
658 return |
593 port_rules = self.external_gateway_snat_rules(ex_gw_port_ip, |
659 port_rules = self.external_gateway_snat_rules(ex_gw_port_ip, |
594 external_dlname) |
660 external_dlname) |
595 for internal_dlname, rules in port_rules.iteritems(): |
661 for internal_dlname, rules in port_rules.iteritems(): |
596 self.pf.add_rules(rules, [external_dlname, internal_dlname]) |
662 self.pf.add_rules(rules, [external_dlname, internal_dlname]) |
597 |
663 |
598 def process_external(self, agent): |
664 def process_external(self, agent): |
599 existing_floating_ips = self.floating_ips |
665 fip_statuses = {} |
600 try: |
666 try: |
601 ex_gw_port = self.get_ex_gw_port() |
667 ex_gw_port = self.get_ex_gw_port() |
602 self._process_external_gateway(ex_gw_port) |
668 self._process_external_gateway(ex_gw_port, agent.pd) |
603 # TODO(Carl) Return after setting existing_floating_ips and |
669 # TODO(Carl) Return after setting existing_floating_ips and |
604 # still call update_fip_statuses? |
670 # still call update_fip_statuses? |
605 if not ex_gw_port: |
671 if not ex_gw_port: |
606 return |
672 return |
607 |
673 |
608 # Once NAT rules for floating IPs are safely in place |
674 # Once NAT rules for floating IPs are safely in place |
609 # configure their addresses on the external gateway port |
675 # configure their addresses on the external gateway port |
610 interface_name = self.get_external_device_name(ex_gw_port['id']) |
676 interface_name = self.get_external_device_name(ex_gw_port['id']) |
611 fip_statuses = self.configure_fip_addresses(interface_name) |
677 fip_statuses = self.configure_fip_addresses(interface_name) |
612 except (n_exc.FloatingIpSetupException, |
678 except n_exc.FloatingIpSetupException: |
613 n_exc.IpTablesApplyException) as e: |
|
614 # All floating IPs must be put in error state |
679 # All floating IPs must be put in error state |
615 LOG.exception(e) |
680 LOG.exception(_LE("Failed to process floating IPs.")) |
616 fip_statuses = self.put_fips_in_error_state() |
681 fip_statuses = self.put_fips_in_error_state() |
617 |
682 finally: |
618 agent.update_fip_statuses(self, existing_floating_ips, fip_statuses) |
683 self.update_fip_statuses(agent, fip_statuses) |
|
684 |
|
685 def process_external_port_address_scope_routing(self, iptables_manager): |
|
686 pass |
|
687 |
|
688 def process_address_scope(self): |
|
689 pass |
619 |
690 |
620 |
691 |
621 class L3NATAgent(l3_agent.L3NATAgentWithStateReport): |
692 class L3NATAgent(l3_agent.L3NATAgentWithStateReport): |
622 OPTS = [ |
693 OPTS = [ |
623 cfg.StrOpt('external_network_datalink', default='net0', |
|
624 help=_("Name of the datalink that connects to " |
|
625 "an external network.")), |
|
626 cfg.BoolOpt('allow_forwarding_between_networks', default=False, |
694 cfg.BoolOpt('allow_forwarding_between_networks', default=False, |
627 help=_("Allow forwarding of packets between tenant's " |
695 help=_("Allow forwarding of packets between tenant's " |
628 "networks")), |
696 "networks")), |
629 ] |
697 ] |
630 |
698 |