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.neutron.spi.INeutronNetworkCRUD;
14 import org.opendaylight.neutron.spi.INeutronPortCRUD;
15 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
16 import org.opendaylight.neutron.spi.NeutronFloatingIP;
17 import org.opendaylight.neutron.spi.NeutronNetwork;
18 import org.opendaylight.neutron.spi.NeutronPort;
19 import org.opendaylight.neutron.spi.NeutronRouter;
20 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
21 import org.opendaylight.neutron.spi.NeutronSubnet;
22 import org.opendaylight.neutron.spi.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.ConfigurationService;
29 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
30 import org.opendaylight.ovsdb.openstack.netvirt.api.MultiTenantAwareRouter;
31 import org.opendaylight.ovsdb.openstack.netvirt.api.NetworkingProviderManager;
32 import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
33 import org.opendaylight.ovsdb.compatibility.plugin.api.OvsdbConfigurationService;
34 import org.opendaylight.ovsdb.compatibility.plugin.api.OvsdbConnectionService;
35 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
36 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
37 import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
38 import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
39 import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
40 import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
41 import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
42 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
43 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
45 import com.google.common.base.Preconditions;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import java.net.InetAddress;
50 import java.net.UnknownHostException;
51 import java.util.HashMap;
52 import java.util.HashSet;
53 import java.util.List;
58 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
59 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
60 * as well as the multi-tenant router forwarding provider.
62 public class NeutronL3Adapter {
67 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
69 // The implementation for each of these services is resolved by the OSGi Service Manager
70 private volatile ConfigurationService configurationService;
71 private volatile TenantNetworkManager tenantNetworkManager;
72 private volatile NetworkingProviderManager networkingProviderManager;
73 private volatile OvsdbConfigurationService ovsdbConfigurationService;
74 private volatile OvsdbConnectionService connectionService;
75 private volatile INeutronNetworkCRUD neutronNetworkCache;
76 private volatile INeutronSubnetCRUD neutronSubnetCache;
77 private volatile INeutronPortCRUD neutronPortCache;
78 private volatile MultiTenantAwareRouter multiTenantAwareRouter;
79 private volatile L3ForwardingProvider l3ForwardingProvider;
80 private volatile InboundNatProvider inboundNatProvider;
81 private volatile OutboundNatProvider outboundNatProvider;
82 private volatile ArpProvider arpProvider;
83 private volatile RoutingProvider routingProvider;
85 private Set<String> inboundIpRewriteCache;
86 private Set<String> outboundIpRewriteCache;
87 private Set<String> inboundIpRewriteExclusionCache;
88 private Set<String> outboundIpRewriteExclusionCache;
89 private Set<String> routerInterfacesCache;
90 private Set<String> staticArpEntryCache;
91 private Set<String> l3ForwardingCache;
92 private Set<String> defaultRouteCache;
93 private Map<String, String> networkIdToRouterMacCache;
94 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
95 private Boolean enabled = false;
98 final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
99 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
100 this.inboundIpRewriteCache = new HashSet<>();
101 this.outboundIpRewriteCache = new HashSet<>();
102 this.inboundIpRewriteExclusionCache = new HashSet<>();
103 this.outboundIpRewriteExclusionCache = new HashSet<>();
104 this.routerInterfacesCache = new HashSet<>();
105 this.staticArpEntryCache = new HashSet<>();
106 this.l3ForwardingCache = new HashSet<>();
107 this.defaultRouteCache = new HashSet<>();
108 this.networkIdToRouterMacCache = new HashMap<>();
109 this.subnetIdToRouterInterfaceCache = new HashMap<>();
112 logger.info("OVSDB L3 forwarding is enabled");
114 logger.debug("OVSDB L3 forwarding is disabled");
119 // Callbacks from OVSDB's northbound handlers
122 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
123 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
128 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
129 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
133 final boolean isDelete = action == Action.DELETE;
135 // Treat the port event as a router interface event if the port belongs to router. This is a
136 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
138 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
139 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
140 NeutronRouter_Interface neutronRouterInterface =
141 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
142 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
143 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
145 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
148 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
149 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
150 // need to do this check here because a router interface is not added to a node until tenant becomes needed
154 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
155 NeutronRouter_Interface neutronRouterInterface =
156 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
157 if (neutronRouterInterface != null) {
158 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
162 this.updateL3ForNeutronPort(neutronPort, isDelete);
166 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
167 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
172 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
173 final NeutronRouter_Interface neutronRouterInterface,
175 logger.debug("Router interface {} got event {}. Subnet {}",
176 neutronRouterInterface.getPortUUID(),
178 neutronRouterInterface.getSubnetUUID());
182 final boolean isDelete = action == Action.DELETE;
184 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
186 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
187 // see if they are affected by l3
189 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
190 boolean currPortShouldBeDeleted = false;
191 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
193 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
194 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
195 currPortShouldBeDeleted = true;
200 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
204 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
206 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
207 neutronFloatingIP.getFixedIPAddress(),
208 neutronFloatingIP.getFloatingIPAddress(),
209 neutronFloatingIP.getFloatingNetworkUUID());
213 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
216 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
217 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
223 // Callbacks from OVSDB's southbound handler
225 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
227 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
228 action, node, intf.getName(), neutronNetwork);
232 // See if there is an external uuid, so we can find the respective neutronPort
233 Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
234 if (externalIds == null) {
237 String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
238 if (neutronPortId == null) {
241 final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
242 if (neutronPort == null) {
243 logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
244 action, node, intf.getName(), neutronNetwork, neutronPortId);
247 this.handleNeutronPortEvent(neutronPort, action);
253 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
255 final String networkUUID = neutronPort.getNetworkUUID();
256 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
258 // If there is no router interface handling the networkUUID, we are done
259 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
263 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
264 // router interface are handled via handleNeutronRouterInterfaceEvent.
265 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
269 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
270 final String providerSegmentationId = neutronNetwork != null ?
271 neutronNetwork.getProviderSegmentationID() : null;
272 final String tenantMac = neutronPort.getMacAddress();
274 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
275 tenantMac == null || tenantMac.isEmpty()) {
276 return; // done: go no further w/out all the info needed...
279 final Action action = isDelete ? Action.DELETE : Action.ADD;
280 List<Node> nodes = connectionService.getNodes();
281 if (nodes.isEmpty()) {
282 logger.trace("updateL3ForNeutronPort has no nodes to work with");
284 for (Node node : nodes) {
285 final Long dpid = getDpid(node);
286 final boolean tenantNetworkPresentInNode =
287 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
288 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
289 final String tenantIpStr = neutronIP.getIpAddress();
290 if (tenantIpStr.isEmpty()) {
294 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
295 // still needed when routing to subnets non-local to node (bug 2076).
296 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
298 // Configure distributed ARP responder. Only needed if tenant network exists in node.
299 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
300 tenantNetworkPresentInNode ? action : Action.DELETE);
305 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
306 String macAddress, String ipStr,
307 Action actionForNode) {
308 // Based on the local cache, figure out whether programming needs to occur. To do this, we
309 // will look at desired action for node.
311 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
312 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
314 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
315 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
316 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
319 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
320 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
321 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
325 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
326 macAddress, ipStr, actionForNode);
327 if (status.isSuccess()) {
329 if (actionForNode == Action.ADD) {
330 l3ForwardingCache.add(cacheKey);
332 l3ForwardingCache.remove(cacheKey);
337 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
340 Action actionForNode) {
343 InetAddress inetAddress = InetAddress.getByName(address);
344 status = l3ForwardingProvider == null ?
345 new Status(StatusCode.SUCCESS) :
346 l3ForwardingProvider.programForwardingTableEntry(node, dpid, providerSegmentationId,
347 inetAddress, macAddress, actionForNode);
348 } catch (UnknownHostException e) {
349 status = new Status(StatusCode.BADREQUEST);
352 if (status.isSuccess()) {
353 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
354 l3ForwardingProvider == null ? "skipped" : "programmed",
355 macAddress, address, node, actionForNode);
357 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
358 macAddress, address, node, actionForNode, status);
365 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
367 Preconditions.checkNotNull(destNeutronRouterInterface);
369 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
370 final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
371 final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
372 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
373 final NeutronNetwork neutronNetwork = subnet != null ?
374 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
375 final String destinationSegmentationId = neutronNetwork != null ?
376 neutronNetwork.getProviderSegmentationID() : null;
377 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
378 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
379 final String cidr = subnet != null ? subnet.getCidr() : null;
380 final int mask = getMaskLenFromCidr(cidr);
382 logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
383 destNeutronRouterInterface, isDelete);
385 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
386 cidr == null || cidr.isEmpty() ||
387 macAddress == null || macAddress.isEmpty() ||
388 ipList == null || ipList.isEmpty()) {
389 logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
390 destinationSegmentationId, cidr, macAddress, ipList);
391 return; // done: go no further w/out all the info needed...
394 final Action action = isDelete ? Action.DELETE : Action.ADD;
396 // Keep cache for finding router's mac from network uuid -- add
399 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
400 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
403 List<Node> nodes = connectionService.getNodes();
404 if (nodes.isEmpty()) {
405 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
407 for (Node node : nodes) {
408 final Long dpid = getDpid(node);
409 final Action actionForNode =
410 tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
411 action : Action.DELETE;
413 for (Neutron_IPs neutronIP : ipList) {
414 final String ipStr = neutronIP.getIpAddress();
415 if (ipStr.isEmpty()) {
416 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
417 node.getID(), ipStr);
421 // Iterate through all other interfaces and add/remove reflexive flows to this interface
423 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
424 programFlowsForNeutronRouterInterfacePair(node, dpid,
425 srcNeutronRouterInterface, destNeutronRouterInterface,
426 neutronNetwork, destinationSegmentationId,
427 macAddress, ipStr, mask, actionForNode,
428 true /*isReflexsive*/);
431 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
434 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
435 // for the external neutron networks.
438 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
439 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
440 cidr, actionForRewriteExclusion);
441 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
442 cidr, actionForRewriteExclusion);
445 // Default route. For non-external subnet, make sure that there is none configured.
447 if (gatewayIp != null && !gatewayIp.isEmpty()) {
448 final Action actionForNodeDefaultRoute =
449 isExternal ? actionForNode : Action.DELETE;
450 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
451 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
452 actionForNodeDefaultRoute);
456 // Keep cache for finding router's mac from network uuid -- remove
459 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
460 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
464 private void programFlowsForNeutronRouterInterfacePair(final Node node,
466 final NeutronRouter_Interface srcNeutronRouterInterface,
467 final NeutronRouter_Interface dstNeutronRouterInterface,
468 final NeutronNetwork dstNeutronNetwork,
469 final String destinationSegmentationId,
470 final String dstMacAddress,
471 final String destIpStr,
473 final Action actionForNode,
474 Boolean isReflexsive) {
475 Preconditions.checkNotNull(srcNeutronRouterInterface);
476 Preconditions.checkNotNull(dstNeutronRouterInterface);
478 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
479 if (sourceSubnetId == null) {
480 logger.error("Could not get provider Subnet ID from router interface {}",
481 srcNeutronRouterInterface.getID());
485 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
486 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
487 if (sourceNetworkId == null) {
488 logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
492 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
493 if (sourceNetwork == null) {
494 logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
498 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
499 // Isolate subnets from different tenants within the same router
502 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
503 if (sourceSegmentationId == null) {
504 logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
507 if (sourceSegmentationId.equals(destinationSegmentationId)) {
512 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
513 dstMacAddress, destIpStr, destMask, actionForNode);
515 // Flip roles src->dst; dst->src
517 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
518 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
519 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
520 final String cidr2 = sourceSubnet.getCidr();
521 final int mask2 = getMaskLenFromCidr(cidr2);
523 if (cidr2 == null || cidr2.isEmpty() ||
524 macAddress2 == null || macAddress2.isEmpty() ||
525 ipList2 == null || ipList2.isEmpty()) {
526 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
527 sourceSegmentationId, cidr2, macAddress2, ipList2);
528 return; // done: go no further w/out all the info needed...
531 for (Neutron_IPs neutronIP2 : ipList2) {
532 final String ipStr2 = neutronIP2.getIpAddress();
533 if (ipStr2.isEmpty()) {
536 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
537 srcNeutronRouterInterface,
538 sourceNetwork, sourceSegmentationId,
539 macAddress2, ipStr2, mask2, actionForNode,
540 false /*isReflexsive*/);
545 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
546 String destinationSegmentationId,
547 String macAddress, String ipStr, int mask,
548 Action actionForNode) {
549 // Based on the local cache, figure out whether programming needs to occur. To do this, we
550 // will look at desired action for node.
552 final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
553 ipStr + "/" + Integer.toString(mask);
554 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
556 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
557 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
558 "action {} is already done",
559 node.getNodeIDString(), sourceSegmentationId, destinationSegmentationId,
560 ipStr, mask, actionForNode);
563 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
564 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
565 "action {} is already done",
566 node.getNodeIDString(), sourceSegmentationId, destinationSegmentationId,
567 ipStr, mask, actionForNode);
571 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
572 macAddress, ipStr, mask, actionForNode);
573 if (status.isSuccess()) {
575 if (actionForNode == Action.ADD) {
576 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
577 routerInterfacesCache.add(cacheKey);
579 // TODO: multiTenantAwareRouter.removeInterface(...);
580 routerInterfacesCache.remove(cacheKey);
585 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
586 String destinationSegmentationId,
588 String address, int mask,
589 Action actionForNode) {
592 InetAddress inetAddress = InetAddress.getByName(address);
593 status = routingProvider == null ?
594 new Status(StatusCode.SUCCESS) :
595 routingProvider.programRouterInterface(node, dpid, sourceSegmentationId, destinationSegmentationId,
596 macAddress, inetAddress, mask, actionForNode);
597 } catch (UnknownHostException e) {
598 status = new Status(StatusCode.BADREQUEST);
601 if (status.isSuccess()) {
602 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
603 routingProvider == null ? "skipped" : "programmed",
604 macAddress, address, mask, node, actionForNode);
606 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
607 macAddress, address, mask, node, actionForNode, status);
612 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
613 String macAddress, String ipStr,
614 Action actionForNode) {
615 // Based on the local cache, figure out whether programming needs to occur. To do this, we
616 // will look at desired action for node.
618 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
619 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
621 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
622 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
623 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
626 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
627 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
628 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
632 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
633 macAddress, ipStr, actionForNode);
634 if (status.isSuccess()) {
636 if (actionForNode == Action.ADD) {
637 staticArpEntryCache.add(cacheKey);
639 staticArpEntryCache.remove(cacheKey);
644 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
647 Action actionForNode) {
650 InetAddress inetAddress = InetAddress.getByName(address);
651 status = arpProvider == null ?
652 new Status(StatusCode.SUCCESS) :
653 arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
654 macAddress, inetAddress, actionForNode);
655 } catch (UnknownHostException e) {
656 status = new Status(StatusCode.BADREQUEST);
659 if (status.isSuccess()) {
660 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
661 arpProvider == null ? "skipped" : "programmed",
662 macAddress, address, node, actionForNode);
664 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
665 macAddress, address, node, actionForNode, status);
670 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
671 final boolean isInbound, String cidr,
672 Action actionForRewriteExclusion) {
673 // Based on the local cache, figure out whether programming needs to occur. To do this, we
674 // will look at desired action for node.
676 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
677 final Boolean isProgrammed = isInbound ?
678 inboundIpRewriteExclusionCache.contains(cacheKey):
679 outboundIpRewriteExclusionCache.contains(cacheKey);
681 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
682 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
683 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
684 actionForRewriteExclusion);
687 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
688 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
689 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
690 actionForRewriteExclusion);
694 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
695 isInbound, actionForRewriteExclusion);
696 if (status.isSuccess()) {
698 if (actionForRewriteExclusion == Action.ADD) {
700 inboundIpRewriteExclusionCache.add(cacheKey);
702 outboundIpRewriteExclusionCache.add(cacheKey);
706 inboundIpRewriteExclusionCache.remove(cacheKey);
708 outboundIpRewriteExclusionCache.remove(cacheKey);
714 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
715 final boolean isInbound, Action actionForNode) {
718 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
719 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
722 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
723 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
727 if (status.isSuccess()) {
728 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
729 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
730 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
731 cidr, node, actionForNode);
733 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
734 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
739 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
740 String defaultGatewayMacAddress, String gatewayIp,
741 Action actionForNodeDefaultRoute) {
742 // Based on the local cache, figure out whether programming needs to occur. To do this, we
743 // will look at desired action for node.
745 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
746 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
748 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
749 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
750 node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
751 actionForNodeDefaultRoute);
754 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
755 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
756 node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
757 actionForNodeDefaultRoute);
761 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
762 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
763 if (status.isSuccess()) {
765 if (actionForNodeDefaultRoute == Action.ADD) {
766 defaultRouteCache.add(cacheKey);
768 defaultRouteCache.remove(cacheKey);
773 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
774 String defaultGatewayMacAddress,
776 Action actionForNodeDefaultRoute) {
777 // TODO: As of Helium, mac address for default gateway is required (bug 1705).
778 if (defaultGatewayMacAddress == null) {
779 logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
780 gatewayIp, node, actionForNodeDefaultRoute);
781 return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
786 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
787 status = routingProvider == null ?
788 new Status(StatusCode.SUCCESS) :
789 routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
790 defaultGatewayMacAddress, inetAddress,
791 actionForNodeDefaultRoute);
792 } catch (UnknownHostException e) {
793 status = new Status(StatusCode.BADREQUEST);
796 if (status.isSuccess()) {
797 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
798 routingProvider == null ? "skipped" : "programmed",
799 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
801 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
802 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
807 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
808 Preconditions.checkNotNull(neutronFloatingIP);
810 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
811 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
813 // If there is no router interface handling the networkUUID, we are done
814 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
818 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
819 final String providerSegmentationId = neutronNetwork != null ?
820 neutronNetwork.getProviderSegmentationID() : null;
821 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
822 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
824 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
825 // routerMacAddress == null || routerMacAddress.isEmpty() ||
826 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
827 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
828 return; // done: go no further w/out all the info needed...
831 final Action action = isDelete ? Action.DELETE : Action.ADD;
832 List<Node> nodes = connectionService.getNodes();
833 if (nodes.isEmpty()) {
834 logger.trace("programFlowsForFloatingIP has no nodes to work with");
836 for (Node node : nodes) {
837 final Long dpid = getDpid(node);
838 final Action actionForNode =
839 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
840 action : Action.DELETE;
842 // Rewrite from float to fixed and vice-versa
844 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
845 floatingIpAddress, fixedIPAddress, actionForNode);
846 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
847 fixedIPAddress, floatingIpAddress, actionForNode);
849 // Respond to arps for the floating ip address
851 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
856 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
857 final boolean isInbound,
858 String matchAddress, String rewriteAddress,
859 Action actionForNode) {
860 // Based on the local cache, figure out whether programming needs to occur. To do this, we
861 // will look at desired action for node.
863 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
864 matchAddress + ":" + rewriteAddress;
865 final Boolean isProgrammed = isInbound ?
866 inboundIpRewriteCache.contains(cacheKey) :
867 outboundIpRewriteCache.contains(cacheKey);
869 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
870 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
872 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
873 matchAddress, rewriteAddress, actionForNode);
876 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
877 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
879 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
880 matchAddress, rewriteAddress, actionForNode);
884 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
885 matchAddress, rewriteAddress, actionForNode);
886 if (status.isSuccess()) {
888 if (actionForNode == Action.ADD) {
890 inboundIpRewriteCache.add(cacheKey);
892 outboundIpRewriteCache.add(cacheKey);
896 inboundIpRewriteCache.remove(cacheKey);
898 outboundIpRewriteCache.remove(cacheKey);
904 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
905 final boolean isInbound,
906 String matchAddress, String rewriteAddress,
907 Action actionForNode) {
910 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
911 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
913 status = inboundNatProvider == null ?
914 new Status(StatusCode.SUCCESS) :
915 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
916 inetMatchAddress, inetRewriteAddress, actionForNode);
918 status = outboundNatProvider == null ?
919 new Status(StatusCode.SUCCESS) :
920 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
921 inetMatchAddress, inetRewriteAddress, actionForNode);
923 } catch (UnknownHostException e) {
924 status = new Status(StatusCode.BADREQUEST);
927 if (status.isSuccess()) {
928 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
929 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
930 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
931 matchAddress, rewriteAddress, node, actionForNode);
933 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
934 (isInbound ? "inbound" : "outbound"),
935 matchAddress, rewriteAddress, node, actionForNode, status);
944 private int getMaskLenFromCidr(String cidr) {
945 if (cidr == null) return 0;
946 String[] splits = cidr.split("/");
947 if (splits.length != 2) return 0;
951 result = Integer.parseInt(splits[1].trim());
953 catch (NumberFormatException nfe)
960 private Long getDpid (Node node) {
961 Preconditions.checkNotNull(ovsdbConfigurationService);
963 String bridgeName = configurationService.getIntegrationBridgeName();
964 String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
965 if (bridgeUuid == null) {
966 logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
971 Row bridgeRow = ovsdbConfigurationService
972 .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
973 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
974 Set<String> dpids = bridge.getDatapathIdColumn().getData();
975 if (dpids == null || dpids.size() == 0) return 0L;
976 return HexEncode.stringToLong((String) dpids.toArray()[0]);
977 } catch (Exception e) {
978 logger.error("Error finding Bridge's OF DPID", e);
983 private String getInternalBridgeUUID (Node node, String bridgeName) {
984 Preconditions.checkNotNull(ovsdbConfigurationService);
986 Map<String, Row> bridgeTable =
987 ovsdbConfigurationService.getRows(node,
988 ovsdbConfigurationService.getTableName(node, Bridge.class));
989 if (bridgeTable == null) return null;
990 for (String key : bridgeTable.keySet()) {
991 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
992 if (bridge.getName().equals(bridgeName)) return key;
994 } catch (Exception e) {
995 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);