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.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.plugin.api.OvsdbConfigurationService;
34 import org.opendaylight.ovsdb.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;
44 import com.google.common.base.Preconditions;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import java.net.InetAddress;
49 import java.net.UnknownHostException;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.List;
57 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
58 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
59 * as well as the multi-tenant router forwarding provider.
61 public class NeutronL3Adapter {
66 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
68 // The implementation for each of these services is resolved by the OSGi Service Manager
69 private volatile ConfigurationService configurationService;
70 private volatile TenantNetworkManager tenantNetworkManager;
71 private volatile NetworkingProviderManager networkingProviderManager;
72 private volatile OvsdbConfigurationService ovsdbConfigurationService;
73 private volatile OvsdbConnectionService connectionService;
74 private volatile INeutronNetworkCRUD neutronNetworkCache;
75 private volatile INeutronSubnetCRUD neutronSubnetCache;
76 private volatile INeutronPortCRUD neutronPortCache;
77 private volatile MultiTenantAwareRouter multiTenantAwareRouter;
78 private volatile L3ForwardingProvider l3ForwardingProvider;
79 private volatile InboundNatProvider inboundNatProvider;
80 private volatile OutboundNatProvider outboundNatProvider;
81 private volatile ArpProvider arpProvider;
82 private volatile RoutingProvider routingProvider;
84 private Set<String> inboundIpRewriteCache;
85 private Set<String> outboundIpRewriteCache;
86 private Set<String> inboundIpRewriteExclusionCache;
87 private Set<String> outboundIpRewriteExclusionCache;
88 private Set<String> routerInterfacesCache;
89 private Set<String> staticArpEntryCache;
90 private Set<String> l3ForwardingCache;
91 private Set<String> defaultRouteCache;
92 private Map<String, String> networkIdToRouterMacCache;
93 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
94 private Boolean enabled = false;
97 final String enabledPropertyStr = System.getProperty("ovsdb.l3.fwd.enabled");
98 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
99 this.inboundIpRewriteCache = new HashSet<>();
100 this.outboundIpRewriteCache = new HashSet<>();
101 this.inboundIpRewriteExclusionCache = new HashSet<>();
102 this.outboundIpRewriteExclusionCache = new HashSet<>();
103 this.routerInterfacesCache = new HashSet<>();
104 this.staticArpEntryCache = new HashSet<>();
105 this.l3ForwardingCache = new HashSet<>();
106 this.defaultRouteCache = new HashSet<>();
107 this.networkIdToRouterMacCache = new HashMap<>();
108 this.subnetIdToRouterInterfaceCache = new HashMap<>();
111 logger.info("OVSDB L3 forwarding is enabled");
113 logger.debug("OVSDB L3 forwarding is disabled");
118 // Callbacks from OVSDB's northbound handlers
121 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
122 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
127 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
128 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
132 final boolean isDelete = action == Action.DELETE;
134 // Treat the port event as a router interface event if the port belongs to router. This is a
135 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
137 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
138 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
139 NeutronRouter_Interface neutronRouterInterface =
140 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
141 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
142 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
144 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
147 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
148 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
149 // need to do this check here because a router interface is not added to a node until tenant becomes needed
153 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
154 NeutronRouter_Interface neutronRouterInterface =
155 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
156 if (neutronRouterInterface != null) {
157 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
161 this.updateL3ForNeutronPort(neutronPort, isDelete);
165 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
166 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
171 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
172 final NeutronRouter_Interface neutronRouterInterface,
174 logger.debug("Router interface {} got event {}. Subnet {}",
175 neutronRouterInterface.getPortUUID(),
177 neutronRouterInterface.getSubnetUUID());
181 final boolean isDelete = action == Action.DELETE;
183 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
185 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
186 // see if they are affected by l3
188 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
189 boolean currPortIsInSameSubnet = false;
190 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
191 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
192 currPortIsInSameSubnet = true;
196 if (currPortIsInSameSubnet == true) {
197 this.updateL3ForNeutronPort(neutronPort, isDelete);
202 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
204 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
205 neutronFloatingIP.getFixedIPAddress(),
206 neutronFloatingIP.getFloatingIPAddress(),
207 neutronFloatingIP.getFloatingNetworkUUID());
211 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
214 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
215 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
221 // Callbacks from OVSDB's southbound handler
223 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
225 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
226 action, node, intf.getName(), neutronNetwork);
230 // See if there is an external uuid, so we can find the respective neutronPort
231 Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
232 if (externalIds == null) {
235 String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
236 if (neutronPortId == null) {
239 final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
240 if (neutronPort == null) {
241 logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
242 action, node, intf.getName(), neutronNetwork, neutronPortId);
245 this.handleNeutronPortEvent(neutronPort, action);
251 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
253 final String networkUUID = neutronPort.getNetworkUUID();
254 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
256 // If there is no router interface handling the networkUUID, we are done
257 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
261 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
262 // router interface are handled via handleNeutronRouterInterfaceEvent.
263 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
267 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
268 final String providerSegmentationId = neutronNetwork != null ?
269 neutronNetwork.getProviderSegmentationID() : null;
270 final String tenantMac = neutronPort.getMacAddress();
272 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
273 tenantMac == null || tenantMac.isEmpty()) {
274 return; // done: go no further w/out all the info needed...
277 final Action action = isDelete ? Action.DELETE : Action.ADD;
278 List<Node> nodes = connectionService.getNodes();
279 if (nodes.isEmpty()) {
280 logger.trace("updateL3ForNeutronPort has no nodes to work with");
282 for (Node node : nodes) {
283 final Long dpid = getDpid(node);
284 final Action actionForNode =
285 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
286 action : Action.DELETE;
287 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
288 final String tenantIpStr = neutronIP.getIpAddress();
289 if (tenantIpStr.isEmpty()) {
294 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
296 // Configure distributed ARP responder
297 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
302 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
303 String macAddress, String ipStr,
304 Action actionForNode) {
305 // Based on the local cache, figure out whether programming needs to occur. To do this, we
306 // will look at desired action for node.
308 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
309 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
311 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
312 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
313 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
316 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
317 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
318 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
322 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
323 macAddress, ipStr, actionForNode);
324 if (status.isSuccess()) {
326 if (actionForNode == Action.ADD) {
327 l3ForwardingCache.add(cacheKey);
329 l3ForwardingCache.remove(cacheKey);
334 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
337 Action actionForNode) {
340 InetAddress inetAddress = InetAddress.getByName(address);
341 status = l3ForwardingProvider == null ?
342 new Status(StatusCode.SUCCESS) :
343 l3ForwardingProvider.programForwardingTableEntry(node, dpid, providerSegmentationId,
344 inetAddress, macAddress, actionForNode);
345 } catch (UnknownHostException e) {
346 status = new Status(StatusCode.BADREQUEST);
349 if (status.isSuccess()) {
350 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
351 l3ForwardingProvider == null ? "skipped" : "programmed",
352 macAddress, address, node, actionForNode);
354 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
355 macAddress, address, node, actionForNode, status);
362 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
364 Preconditions.checkNotNull(destNeutronRouterInterface);
366 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
367 final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
368 final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
369 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
370 final NeutronNetwork neutronNetwork = subnet != null ?
371 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
372 final String destinationSegmentationId = neutronNetwork != null ?
373 neutronNetwork.getProviderSegmentationID() : null;
374 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
375 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
376 final String cidr = subnet != null ? subnet.getCidr() : null;
377 final int mask = getMaskLenFromCidr(cidr);
379 logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
380 destNeutronRouterInterface, isDelete);
382 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
383 cidr == null || cidr.isEmpty() ||
384 macAddress == null || macAddress.isEmpty() ||
385 ipList == null || ipList.isEmpty()) {
386 logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
387 destinationSegmentationId, cidr, macAddress, ipList);
388 return; // done: go no further w/out all the info needed...
391 final Action action = isDelete ? Action.DELETE : Action.ADD;
393 // Keep cache for finding router's mac from network uuid -- add
396 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
397 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
400 List<Node> nodes = connectionService.getNodes();
401 if (nodes.isEmpty()) {
402 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
404 for (Node node : nodes) {
405 final Long dpid = getDpid(node);
406 final Action actionForNode =
407 tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
408 action : Action.DELETE;
410 for (Neutron_IPs neutronIP : ipList) {
411 final String ipStr = neutronIP.getIpAddress();
412 if (ipStr.isEmpty()) {
413 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
414 node.getID(), ipStr);
418 // Iterate through all other interfaces and add/remove reflexive flows to this interface
420 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
421 programFlowsForNeutronRouterInterfacePair(node, dpid,
422 srcNeutronRouterInterface, destNeutronRouterInterface,
423 neutronNetwork, destinationSegmentationId,
424 macAddress, ipStr, mask, actionForNode,
425 true /*isReflexsive*/);
428 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
431 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
432 // for the external neutron networks.
435 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
436 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
437 cidr, actionForRewriteExclusion);
438 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
439 cidr, actionForRewriteExclusion);
442 // Default route. For non-external subnet, make sure that there is none configured.
444 if (gatewayIp != null && !gatewayIp.isEmpty()) {
445 final Action actionForNodeDefaultRoute =
446 isExternal ? actionForNode : Action.DELETE;
447 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
448 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
449 actionForNodeDefaultRoute);
453 // Keep cache for finding router's mac from network uuid -- remove
456 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
457 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
461 private void programFlowsForNeutronRouterInterfacePair(final Node node,
463 final NeutronRouter_Interface srcNeutronRouterInterface,
464 final NeutronRouter_Interface dstNeutronRouterInterface,
465 final NeutronNetwork dstNeutronNetwork,
466 final String destinationSegmentationId,
467 final String dstMacAddress,
468 final String destIpStr,
470 final Action actionForNode,
471 Boolean isReflexsive) {
472 Preconditions.checkNotNull(srcNeutronRouterInterface);
473 Preconditions.checkNotNull(dstNeutronRouterInterface);
475 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
476 if (sourceSubnetId == null) {
477 logger.error("Could not get provider Subnet ID from router interface {}",
478 srcNeutronRouterInterface.getID());
482 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
483 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
484 if (sourceNetworkId == null) {
485 logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
489 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
490 if (sourceNetwork == null) {
491 logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
495 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
496 // Isolate subnets from different tenants within the same router
499 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
500 if (sourceSegmentationId == null) {
501 logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
504 if (sourceSegmentationId.equals(destinationSegmentationId)) {
509 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
510 dstMacAddress, destIpStr, destMask, actionForNode);
512 // Flip roles src->dst; dst->src
514 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
515 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
516 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
517 final String cidr2 = sourceSubnet.getCidr();
518 final int mask2 = getMaskLenFromCidr(cidr2);
520 if (cidr2 == null || cidr2.isEmpty() ||
521 macAddress2 == null || macAddress2.isEmpty() ||
522 ipList2 == null || ipList2.isEmpty()) {
523 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
524 sourceSegmentationId, cidr2, macAddress2, ipList2);
525 return; // done: go no further w/out all the info needed...
528 for (Neutron_IPs neutronIP2 : ipList2) {
529 final String ipStr2 = neutronIP2.getIpAddress();
530 if (ipStr2.isEmpty()) {
533 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
534 srcNeutronRouterInterface,
535 sourceNetwork, sourceSegmentationId,
536 macAddress2, ipStr2, mask2, actionForNode,
537 false /*isReflexsive*/);
542 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
543 String destinationSegmentationId,
544 String macAddress, String ipStr, int mask,
545 Action actionForNode) {
546 // Based on the local cache, figure out whether programming needs to occur. To do this, we
547 // will look at desired action for node.
549 final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
550 ipStr + "/" + Integer.toString(mask);
551 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
553 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
554 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
555 "action {} is already done",
556 node.getNodeIDString(), sourceSegmentationId, destinationSegmentationId,
557 ipStr, mask, actionForNode);
560 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
561 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
562 "action {} is already done",
563 node.getNodeIDString(), sourceSegmentationId, destinationSegmentationId,
564 ipStr, mask, actionForNode);
568 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
569 macAddress, ipStr, mask, actionForNode);
570 if (status.isSuccess()) {
572 if (actionForNode == Action.ADD) {
573 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
574 routerInterfacesCache.add(cacheKey);
576 // TODO: multiTenantAwareRouter.removeInterface(...);
577 routerInterfacesCache.remove(cacheKey);
582 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
583 String destinationSegmentationId,
585 String address, int mask,
586 Action actionForNode) {
589 InetAddress inetAddress = InetAddress.getByName(address);
590 status = routingProvider == null ?
591 new Status(StatusCode.SUCCESS) :
592 routingProvider.programRouterInterface(node, dpid, sourceSegmentationId, destinationSegmentationId,
593 macAddress, inetAddress, mask, actionForNode);
594 } catch (UnknownHostException e) {
595 status = new Status(StatusCode.BADREQUEST);
598 if (status.isSuccess()) {
599 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
600 routingProvider == null ? "skipped" : "programmed",
601 macAddress, address, mask, node, actionForNode);
603 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
604 macAddress, address, mask, node, actionForNode, status);
609 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
610 String macAddress, String ipStr,
611 Action actionForNode) {
612 // Based on the local cache, figure out whether programming needs to occur. To do this, we
613 // will look at desired action for node.
615 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
616 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
618 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
619 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
620 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
623 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
624 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
625 node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
629 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
630 macAddress, ipStr, actionForNode);
631 if (status.isSuccess()) {
633 if (actionForNode == Action.ADD) {
634 staticArpEntryCache.add(cacheKey);
636 staticArpEntryCache.remove(cacheKey);
641 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
644 Action actionForNode) {
647 InetAddress inetAddress = InetAddress.getByName(address);
648 status = arpProvider == null ?
649 new Status(StatusCode.SUCCESS) :
650 arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
651 macAddress, inetAddress, actionForNode);
652 } catch (UnknownHostException e) {
653 status = new Status(StatusCode.BADREQUEST);
656 if (status.isSuccess()) {
657 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
658 arpProvider == null ? "skipped" : "programmed",
659 macAddress, address, node, actionForNode);
661 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
662 macAddress, address, node, actionForNode, status);
667 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
668 final boolean isInbound, String cidr,
669 Action actionForRewriteExclusion) {
670 // Based on the local cache, figure out whether programming needs to occur. To do this, we
671 // will look at desired action for node.
673 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
674 final Boolean isProgrammed = isInbound ?
675 inboundIpRewriteExclusionCache.contains(cacheKey):
676 outboundIpRewriteExclusionCache.contains(cacheKey);
678 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
679 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
680 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
681 actionForRewriteExclusion);
684 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
685 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
686 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
687 actionForRewriteExclusion);
691 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
692 isInbound, actionForRewriteExclusion);
693 if (status.isSuccess()) {
695 if (actionForRewriteExclusion == Action.ADD) {
697 inboundIpRewriteExclusionCache.add(cacheKey);
699 outboundIpRewriteExclusionCache.add(cacheKey);
703 inboundIpRewriteExclusionCache.remove(cacheKey);
705 outboundIpRewriteExclusionCache.remove(cacheKey);
711 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
712 final boolean isInbound, Action actionForNode) {
715 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
716 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
719 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
720 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
724 if (status.isSuccess()) {
725 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
726 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
727 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
728 cidr, node, actionForNode);
730 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
731 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
736 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
737 String defaultGatewayMacAddress, String gatewayIp,
738 Action actionForNodeDefaultRoute) {
739 // Based on the local cache, figure out whether programming needs to occur. To do this, we
740 // will look at desired action for node.
742 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
743 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
745 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
746 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
747 node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
748 actionForNodeDefaultRoute);
751 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
752 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
753 node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
754 actionForNodeDefaultRoute);
758 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
759 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
760 if (status.isSuccess()) {
762 if (actionForNodeDefaultRoute == Action.ADD) {
763 defaultRouteCache.add(cacheKey);
765 defaultRouteCache.remove(cacheKey);
770 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
771 String defaultGatewayMacAddress,
773 Action actionForNodeDefaultRoute) {
774 // TODO: As of Helium, mac address for default gateway is required (bug 1705).
775 if (defaultGatewayMacAddress == null) {
776 logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
777 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
778 return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
783 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
784 status = routingProvider == null ?
785 new Status(StatusCode.SUCCESS) :
786 routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
787 defaultGatewayMacAddress, inetAddress,
788 actionForNodeDefaultRoute);
789 } catch (UnknownHostException e) {
790 status = new Status(StatusCode.BADREQUEST);
793 if (status.isSuccess()) {
794 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
795 routingProvider == null ? "skipped" : "programmed",
796 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
798 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
799 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
804 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
805 Preconditions.checkNotNull(neutronFloatingIP);
807 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
808 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
810 // If there is no router interface handling the networkUUID, we are done
811 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
815 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
816 final String providerSegmentationId = neutronNetwork != null ?
817 neutronNetwork.getProviderSegmentationID() : null;
818 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
819 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
821 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
822 // routerMacAddress == null || routerMacAddress.isEmpty() ||
823 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
824 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
825 return; // done: go no further w/out all the info needed...
828 final Action action = isDelete ? Action.DELETE : Action.ADD;
829 List<Node> nodes = connectionService.getNodes();
830 if (nodes.isEmpty()) {
831 logger.trace("programFlowsForFloatingIP has no nodes to work with");
833 for (Node node : nodes) {
834 final Long dpid = getDpid(node);
835 final Action actionForNode =
836 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
837 action : Action.DELETE;
839 // Rewrite from float to fixed and vice-versa
841 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
842 floatingIpAddress, fixedIPAddress, actionForNode);
843 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
844 fixedIPAddress, floatingIpAddress, actionForNode);
846 // Respond to arps for the floating ip address
848 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
853 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
854 final boolean isInbound,
855 String matchAddress, String rewriteAddress,
856 Action actionForNode) {
857 // Based on the local cache, figure out whether programming needs to occur. To do this, we
858 // will look at desired action for node.
860 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
861 matchAddress + ":" + rewriteAddress;
862 final Boolean isProgrammed = isInbound ?
863 inboundIpRewriteCache.contains(cacheKey) :
864 outboundIpRewriteCache.contains(cacheKey);
866 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
867 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
869 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
870 matchAddress, rewriteAddress, actionForNode);
873 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
874 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
876 node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
877 matchAddress, rewriteAddress, actionForNode);
881 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
882 matchAddress, rewriteAddress, actionForNode);
883 if (status.isSuccess()) {
885 if (actionForNode == Action.ADD) {
887 inboundIpRewriteCache.add(cacheKey);
889 outboundIpRewriteCache.add(cacheKey);
893 inboundIpRewriteCache.remove(cacheKey);
895 outboundIpRewriteCache.remove(cacheKey);
901 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
902 final boolean isInbound,
903 String matchAddress, String rewriteAddress,
904 Action actionForNode) {
907 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
908 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
910 status = inboundNatProvider == null ?
911 new Status(StatusCode.SUCCESS) :
912 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
913 inetMatchAddress, inetRewriteAddress, actionForNode);
915 status = outboundNatProvider == null ?
916 new Status(StatusCode.SUCCESS) :
917 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
918 inetMatchAddress, inetRewriteAddress, actionForNode);
920 } catch (UnknownHostException e) {
921 status = new Status(StatusCode.BADREQUEST);
924 if (status.isSuccess()) {
925 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
926 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
927 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
928 matchAddress, rewriteAddress, node, actionForNode);
930 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
931 (isInbound ? "inbound" : "outbound"),
932 matchAddress, rewriteAddress, node, actionForNode, status);
941 private int getMaskLenFromCidr(String cidr) {
942 if (cidr == null) return 0;
943 String[] splits = cidr.split("/");
944 if (splits.length != 2) return 0;
948 result = Integer.parseInt(splits[1].trim());
950 catch (NumberFormatException nfe)
957 private Long getDpid (Node node) {
958 Preconditions.checkNotNull(ovsdbConfigurationService);
960 String bridgeName = configurationService.getIntegrationBridgeName();
961 String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
962 if (bridgeUuid == null) {
963 logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
968 Row bridgeRow = ovsdbConfigurationService
969 .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
970 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
971 Set<String> dpids = bridge.getDatapathIdColumn().getData();
972 if (dpids == null || dpids.size() == 0) return 0L;
973 return HexEncode.stringToLong((String) dpids.toArray()[0]);
974 } catch (Exception e) {
975 logger.error("Error finding Bridge's OF DPID", e);
980 private String getInternalBridgeUUID (Node node, String bridgeName) {
981 Preconditions.checkNotNull(ovsdbConfigurationService);
983 Map<String, Row> bridgeTable =
984 ovsdbConfigurationService.getRows(node,
985 ovsdbConfigurationService.getTableName(node, Bridge.class));
986 if (bridgeTable == null) return null;
987 for (String key : bridgeTable.keySet()) {
988 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
989 if (bridge.getName().equals(bridgeName)) return key;
991 } catch (Exception e) {
992 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);