2 * Copyright (C) 2014 Red Hat, Inc.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 * Authors : Dave Tucker, Flavio Fernandes
11 package org.opendaylight.ovsdb.openstack.netvirt.impl;
13 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
14 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
15 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
16 import org.opendaylight.controller.networkconfig.neutron.NeutronFloatingIP;
17 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
18 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
19 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter;
20 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface;
21 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
22 import org.opendaylight.controller.networkconfig.neutron.Neutron_IPs;
23 import org.opendaylight.controller.sal.core.Node;
24 import org.opendaylight.controller.sal.utils.HexEncode;
25 import org.opendaylight.controller.sal.utils.Status;
26 import org.opendaylight.controller.sal.utils.StatusCode;
27 import org.opendaylight.ovsdb.lib.notation.Row;
28 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
29 import org.opendaylight.ovsdb.openstack.netvirt.api.MultiTenantAwareRouter;
30 import org.opendaylight.ovsdb.openstack.netvirt.api.NetworkingProviderManager;
31 import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
32 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
33 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
34 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
35 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
36 import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
37 import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
38 import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
39 import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
40 import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
41 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
43 import com.google.common.base.Preconditions;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import java.net.InetAddress;
48 import java.net.UnknownHostException;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.List;
56 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
57 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
58 * as well as the multi-tenant router forwarding provider.
60 public class NeutronL3Adapter {
65 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
67 // The implementation for each of these services is resolved by the OSGi Service Manager
68 private volatile org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService configurationService;
69 private volatile TenantNetworkManager tenantNetworkManager;
70 private volatile NetworkingProviderManager networkingProviderManager;
71 private volatile OvsdbConfigurationService ovsdbConfigurationService;
72 private volatile OvsdbConnectionService connectionService;
73 private volatile INeutronNetworkCRUD neutronNetworkCache;
74 private volatile INeutronSubnetCRUD neutronSubnetCache;
75 private volatile INeutronPortCRUD neutronPortCache;
76 private volatile MultiTenantAwareRouter multiTenantAwareRouter;
77 private volatile L3ForwardingProvider l3ForwardingProvider;
78 private volatile InboundNatProvider inboundNatProvider;
79 private volatile OutboundNatProvider outboundNatProvider;
80 private volatile ArpProvider arpProvider;
81 private volatile RoutingProvider routingProvider;
83 private Set<String> inboundIpRewriteCache;
84 private Set<String> outboundIpRewriteCache;
85 private Set<String> inboundIpRewriteExclusionCache;
86 private Set<String> outboundIpRewriteExclusionCache;
87 private Set<String> routerInterfacesCache;
88 private Set<String> staticArpEntryCache;
89 private Set<String> l3ForwardingCache;
90 private Set<String> defaultRouteCache;
91 private Map<String, String> networkIdToRouterMacCache;
92 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
93 private Boolean enabled = false;
96 final String enabledPropertyStr = System.getProperty("ovsdb.l3.fwd.enabled");
97 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
98 this.inboundIpRewriteCache = new HashSet<>();
99 this.outboundIpRewriteCache = new HashSet<>();
100 this.inboundIpRewriteExclusionCache = new HashSet<>();
101 this.outboundIpRewriteExclusionCache = new HashSet<>();
102 this.routerInterfacesCache = new HashSet<>();
103 this.staticArpEntryCache = new HashSet<>();
104 this.l3ForwardingCache = new HashSet<>();
105 this.defaultRouteCache = new HashSet<>();
106 this.networkIdToRouterMacCache = new HashMap<>();
107 this.subnetIdToRouterInterfaceCache = new HashMap<>();
110 logger.info("OVSDB L3 forwarding is enabled");
112 logger.debug("OVSDB L3 forwarding is disabled");
117 // Callbacks from OVSDB's northbound handlers
120 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
121 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
126 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
127 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
131 final boolean isDelete = action == Action.DELETE;
133 // Treat the port event as a router interface event if the port belongs to router. This is a
134 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
136 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
137 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
138 NeutronRouter_Interface neutronRouterInterface =
139 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
140 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
141 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
143 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
146 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
147 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
148 // need to do this check here because a router interface is not added to a node until tenant becomes needed
152 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
153 NeutronRouter_Interface neutronRouterInterface =
154 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
155 if (neutronRouterInterface != null) {
156 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
160 this.updateL3ForNeutronPort(neutronPort, isDelete);
164 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
165 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
170 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
171 final NeutronRouter_Interface neutronRouterInterface,
173 logger.debug("Router interface {} got event {}. Subnet {}",
174 neutronRouterInterface.getPortUUID(),
176 neutronRouterInterface.getSubnetUUID());
180 final boolean isDelete = action == Action.DELETE;
182 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
184 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
185 // see if they are affected by l3
187 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
188 boolean currPortIsInSameSubnet = false;
189 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
190 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
191 currPortIsInSameSubnet = true;
195 if (currPortIsInSameSubnet == true) {
196 this.updateL3ForNeutronPort(neutronPort, isDelete);
201 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
203 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
204 neutronFloatingIP.getFixedIPAddress(),
205 neutronFloatingIP.getFloatingIPAddress(),
206 neutronFloatingIP.getFloatingNetworkUUID());
210 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
213 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
214 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
220 // Callbacks from OVSDB's southbound handler
222 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
224 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
225 action, node, intf.getName(), neutronNetwork);
229 // See if there is an external uuid, so we can find the respective neutronPort
230 Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
231 if (externalIds == null) {
234 String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
235 if (neutronPortId == null) {
238 final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
239 if (neutronPort == null) {
240 logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
241 action, node, intf.getName(), neutronNetwork, neutronPortId);
244 this.handleNeutronPortEvent(neutronPort, action);
250 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
252 final String networkUUID = neutronPort.getNetworkUUID();
253 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
255 // If there is no router interface handling the networkUUID, we are done
256 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
260 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
261 // router interface are handled via handleNeutronRouterInterfaceEvent.
262 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
266 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
267 final String providerSegmentationId = neutronNetwork != null ?
268 neutronNetwork.getProviderSegmentationID() : null;
269 final String tenantMac = neutronPort.getMacAddress();
271 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
272 tenantMac == null || tenantMac.isEmpty()) {
273 return; // done: go no further w/out all the info needed...
276 final Action action = isDelete ? Action.DELETE : Action.ADD;
277 List<Node> nodes = connectionService.getNodes();
278 if (nodes.isEmpty()) {
279 logger.trace("updateL3ForNeutronPort has no nodes to work with");
281 for (Node node : nodes) {
282 final Long dpid = getDpid(node);
283 final Action actionForNode =
284 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
285 action : Action.DELETE;
286 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
287 final String tenantIpStr = neutronIP.getIpAddress();
288 if (tenantIpStr.isEmpty()) {
293 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
295 // Configure distributed ARP responder
296 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
301 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
302 String macAddress, String ipStr,
303 Action actionForNode) {
304 // Based on the local cache, figure out whether programming needs to occur. To do this, we
305 // will look at desired action for node.
307 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
308 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
310 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
311 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
312 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
315 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
316 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
317 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
321 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
322 macAddress, ipStr, actionForNode);
323 if (status.isSuccess()) {
325 if (actionForNode == Action.ADD) {
326 l3ForwardingCache.add(cacheKey);
328 l3ForwardingCache.remove(cacheKey);
333 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
336 Action actionForNode) {
339 InetAddress inetAddress = InetAddress.getByName(address);
340 status = l3ForwardingProvider == null ?
341 new Status(StatusCode.SUCCESS) :
342 l3ForwardingProvider.programForwardingTableEntry(node, dpid, providerSegmentationId,
343 inetAddress, macAddress, actionForNode);
344 } catch (UnknownHostException e) {
345 status = new Status(StatusCode.BADREQUEST);
348 if (status.isSuccess()) {
349 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
350 l3ForwardingProvider == null ? "skipped" : "programmed",
351 macAddress, address, node, actionForNode);
353 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
354 macAddress, address, node, actionForNode, status);
361 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface neutronRouterInterface,
363 Preconditions.checkNotNull(neutronRouterInterface);
365 final NeutronPort neutronPort = neutronPortCache.getPort(neutronRouterInterface.getPortUUID());
366 final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
367 final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
368 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(neutronRouterInterface.getSubnetUUID());
369 final NeutronNetwork neutronNetwork = subnet != null ?
370 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
371 final String providerSegmentationId = neutronNetwork != null ?
372 neutronNetwork.getProviderSegmentationID() : null;
373 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
374 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
375 final String cidr = subnet != null ? subnet.getCidr() : null;
376 final int mask = getMaskLenFromCidr(cidr);
378 logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
379 neutronRouterInterface, isDelete);
381 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
382 cidr == null || cidr.isEmpty() ||
383 macAddress == null || macAddress.isEmpty() ||
384 ipList == null || ipList.isEmpty()) {
385 logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
386 providerSegmentationId, cidr, macAddress, ipList);
387 return; // done: go no further w/out all the info needed...
390 final Action action = isDelete ? Action.DELETE : Action.ADD;
392 // Keep cache for finding router's mac from network uuid
395 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
396 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
398 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
399 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), neutronRouterInterface);
402 List<Node> nodes = connectionService.getNodes();
403 if (nodes.isEmpty()) {
404 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
406 for (Node node : nodes) {
407 final Long dpid = getDpid(node);
408 final Action actionForNode =
409 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
410 action : Action.DELETE;
412 for (Neutron_IPs neutronIP : ipList) {
413 final String ipStr = neutronIP.getIpAddress();
414 if (ipStr.isEmpty()) {
415 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
416 node.getID(), ipStr);
419 programRouterInterfaceStage1(node, dpid, providerSegmentationId, macAddress, ipStr, mask, actionForNode);
420 programStaticArpStage1(node, dpid, providerSegmentationId, macAddress, ipStr, actionForNode);
423 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
424 // for the external neutron networks.
427 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
428 programIpRewriteExclusionStage1(node, dpid, providerSegmentationId, true /* isInbound */,
429 cidr, actionForRewriteExclusion);
430 programIpRewriteExclusionStage1(node, dpid, providerSegmentationId, false /* isInbound */,
431 cidr, actionForRewriteExclusion);
434 // Default route. For non-external subnet, make sure that there is none configured.
436 if (gatewayIp != null && !gatewayIp.isEmpty()) {
437 final Action actionForNodeDefaultRoute =
438 isExternal ? actionForNode : Action.DELETE;
439 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
440 programDefaultRouteStage1(node, dpid, providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
441 actionForNodeDefaultRoute);
446 private void programRouterInterfaceStage1(Node node, Long dpid, String providerSegmentationId,
447 String macAddress, String ipStr, int mask,
448 Action actionForNode) {
449 // Based on the local cache, figure out whether programming needs to occur. To do this, we
450 // will look at desired action for node.
452 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
453 ipStr + "/" + Integer.toString(mask);
454 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
456 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
457 logger.trace("programRouterInterfaceStage1 for node {} providerId {} mac {} ip {} mask {} action {}" +
459 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, mask, actionForNode);
462 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
463 logger.trace("programRouterInterfaceStage1 for node {} providerId {} mac {} ip {} mask {} action {}" +
465 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, mask, actionForNode);
469 Status status = this.programRouterInterfaceStage2(node, dpid, providerSegmentationId,
470 macAddress, ipStr, mask, actionForNode);
471 if (status.isSuccess()) {
473 if (actionForNode == Action.ADD) {
474 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
475 routerInterfacesCache.add(cacheKey);
477 // TODO: multiTenantAwareRouter.removeInterface(...);
478 routerInterfacesCache.remove(cacheKey);
483 private Status programRouterInterfaceStage2(Node node, Long dpid, String providerSegmentationId,
485 String address, int mask,
486 Action actionForNode) {
489 InetAddress inetAddress = InetAddress.getByName(address);
490 status = routingProvider == null ?
491 new Status(StatusCode.SUCCESS) :
492 routingProvider.programRouterInterface(node, dpid, providerSegmentationId,
493 macAddress, inetAddress, mask, actionForNode);
494 } catch (UnknownHostException e) {
495 status = new Status(StatusCode.BADREQUEST);
498 if (status.isSuccess()) {
499 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
500 routingProvider == null ? "skipped" : "programmed",
501 macAddress, address, mask, node, actionForNode);
503 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
504 macAddress, address, mask, node, actionForNode, status);
509 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
510 String macAddress, String ipStr,
511 Action actionForNode) {
512 // Based on the local cache, figure out whether programming needs to occur. To do this, we
513 // will look at desired action for node.
515 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
516 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
518 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
519 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
520 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
523 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
524 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
525 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
529 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
530 macAddress, ipStr, actionForNode);
531 if (status.isSuccess()) {
533 if (actionForNode == Action.ADD) {
534 staticArpEntryCache.add(cacheKey);
536 staticArpEntryCache.remove(cacheKey);
541 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
544 Action actionForNode) {
547 InetAddress inetAddress = InetAddress.getByName(address);
548 status = arpProvider == null ?
549 new Status(StatusCode.SUCCESS) :
550 arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
551 macAddress, inetAddress, actionForNode);
552 } catch (UnknownHostException e) {
553 status = new Status(StatusCode.BADREQUEST);
556 if (status.isSuccess()) {
557 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
558 arpProvider == null ? "skipped" : "programmed",
559 macAddress, address, node, actionForNode);
561 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
562 macAddress, address, node, actionForNode, status);
567 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
568 final boolean isInbound, String cidr,
569 Action actionForRewriteExclusion) {
570 // Based on the local cache, figure out whether programming needs to occur. To do this, we
571 // will look at desired action for node.
573 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
574 final Boolean isProgrammed = isInbound ?
575 inboundIpRewriteExclusionCache.contains(cacheKey):
576 outboundIpRewriteExclusionCache.contains(cacheKey);
578 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
579 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
580 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
581 actionForRewriteExclusion);
584 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
585 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
586 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
587 actionForRewriteExclusion);
591 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
592 isInbound, actionForRewriteExclusion);
593 if (status.isSuccess()) {
595 if (actionForRewriteExclusion == Action.ADD) {
597 inboundIpRewriteExclusionCache.add(cacheKey);
599 outboundIpRewriteExclusionCache.add(cacheKey);
603 inboundIpRewriteExclusionCache.remove(cacheKey);
605 outboundIpRewriteExclusionCache.remove(cacheKey);
611 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
612 final boolean isInbound, Action actionForNode) {
615 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
616 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
619 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
620 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
624 if (status.isSuccess()) {
625 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
626 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
627 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
628 cidr, node, actionForNode);
630 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
631 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
636 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
637 String defaultGatewayMacAddress, String gatewayIp,
638 Action actionForNodeDefaultRoute) {
639 // Based on the local cache, figure out whether programming needs to occur. To do this, we
640 // will look at desired action for node.
642 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
643 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
645 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
646 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
647 node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
648 actionForNodeDefaultRoute);
651 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
652 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
653 node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
654 actionForNodeDefaultRoute);
658 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
659 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
660 if (status.isSuccess()) {
662 if (actionForNodeDefaultRoute == Action.ADD) {
663 defaultRouteCache.add(cacheKey);
665 defaultRouteCache.remove(cacheKey);
670 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
671 String defaultGatewayMacAddress,
673 Action actionForNodeDefaultRoute) {
676 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
677 status = routingProvider == null ?
678 new Status(StatusCode.SUCCESS) :
679 routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
680 defaultGatewayMacAddress, inetAddress,
681 actionForNodeDefaultRoute);
682 } catch (UnknownHostException e) {
683 status = new Status(StatusCode.BADREQUEST);
686 if (status.isSuccess()) {
687 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
688 routingProvider == null ? "skipped" : "programmed",
689 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
691 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
692 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
697 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
698 Preconditions.checkNotNull(neutronFloatingIP);
700 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
701 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
703 // If there is no router interface handling the networkUUID, we are done
704 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
708 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
709 final String providerSegmentationId = neutronNetwork != null ?
710 neutronNetwork.getProviderSegmentationID() : null;
711 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
712 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
714 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
715 // routerMacAddress == null || routerMacAddress.isEmpty() ||
716 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
717 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
718 return; // done: go no further w/out all the info needed...
721 final Action action = isDelete ? Action.DELETE : Action.ADD;
722 List<Node> nodes = connectionService.getNodes();
723 if (nodes.isEmpty()) {
724 logger.trace("programFlowsForFloatingIP has no nodes to work with");
726 for (Node node : nodes) {
727 final Long dpid = getDpid(node);
728 final Action actionForNode =
729 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
730 action : Action.DELETE;
732 // Rewrite from float to fixed and vice-versa
734 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
735 floatingIpAddress, fixedIPAddress, actionForNode);
736 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
737 fixedIPAddress, floatingIpAddress, actionForNode);
739 // Respond to arps for the floating ip address
741 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
746 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
747 final boolean isInbound,
748 String matchAddress, String rewriteAddress,
749 Action actionForNode) {
750 // Based on the local cache, figure out whether programming needs to occur. To do this, we
751 // will look at desired action for node.
753 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
754 matchAddress + ":" + rewriteAddress;
755 final Boolean isProgrammed = isInbound ?
756 inboundIpRewriteCache.contains(cacheKey) :
757 outboundIpRewriteCache.contains(cacheKey);
759 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
760 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
762 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
763 matchAddress, rewriteAddress, actionForNode);
766 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
767 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
769 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
770 matchAddress, rewriteAddress, actionForNode);
774 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
775 matchAddress, rewriteAddress, actionForNode);
776 if (status.isSuccess()) {
778 if (actionForNode == Action.ADD) {
780 inboundIpRewriteCache.add(cacheKey);
782 outboundIpRewriteCache.add(cacheKey);
786 inboundIpRewriteCache.remove(cacheKey);
788 outboundIpRewriteCache.remove(cacheKey);
794 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
795 final boolean isInbound,
796 String matchAddress, String rewriteAddress,
797 Action actionForNode) {
800 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
801 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
803 status = inboundNatProvider == null ?
804 new Status(StatusCode.SUCCESS) :
805 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
806 inetMatchAddress, inetRewriteAddress, actionForNode);
808 status = outboundNatProvider == null ?
809 new Status(StatusCode.SUCCESS) :
810 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
811 inetMatchAddress, inetRewriteAddress, actionForNode);
813 } catch (UnknownHostException e) {
814 status = new Status(StatusCode.BADREQUEST);
817 if (status.isSuccess()) {
818 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
819 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
820 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
821 matchAddress, rewriteAddress, node, actionForNode);
823 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
824 (isInbound ? "inbound" : "outbound"),
825 matchAddress, rewriteAddress, node, actionForNode, status);
834 private int getMaskLenFromCidr(String cidr) {
835 if (cidr == null) return 0;
836 String[] splits = cidr.split("/");
837 if (splits.length != 2) return 0;
841 result = Integer.parseInt(splits[1].trim());
843 catch (NumberFormatException nfe)
850 private Long getDpid (Node node) {
851 Preconditions.checkNotNull(ovsdbConfigurationService);
853 String bridgeName = configurationService.getIntegrationBridgeName();
854 String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
855 if (bridgeUuid == null) {
856 logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
861 Row bridgeRow = ovsdbConfigurationService
862 .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
863 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
864 Set<String> dpids = bridge.getDatapathIdColumn().getData();
865 if (dpids == null || dpids.size() == 0) return 0L;
866 return HexEncode.stringToLong((String) dpids.toArray()[0]);
867 } catch (Exception e) {
868 logger.error("Error finding Bridge's OF DPID", e);
873 private String getInternalBridgeUUID (Node node, String bridgeName) {
874 Preconditions.checkNotNull(ovsdbConfigurationService);
876 Map<String, Row> bridgeTable =
877 ovsdbConfigurationService.getRows(node,
878 ovsdbConfigurationService.getTableName(node, Bridge.class));
879 if (bridgeTable == null) return null;
880 for (String key : bridgeTable.keySet()) {
881 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
882 if (bridge.getName().equals(bridgeName)) return key;
884 } catch (Exception e) {
885 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);