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.ovsdb.lib.notation.Row;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
25 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
26 import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
27 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
28 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
29 import org.opendaylight.ovsdb.plugin.api.Status;
30 import org.opendaylight.ovsdb.plugin.api.StatusCode;
31 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
32 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
33 import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
34 import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
35 import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
36 import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
37 import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
38 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
39 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
40 import org.opendaylight.ovsdb.utils.mdsal.node.StringConvertor;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
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 ConfigurationService configurationService;
69 private volatile TenantNetworkManager tenantNetworkManager;
70 private volatile OvsdbConfigurationService ovsdbConfigurationService;
71 private volatile OvsdbConnectionService connectionService;
72 private volatile INeutronNetworkCRUD neutronNetworkCache;
73 private volatile INeutronSubnetCRUD neutronSubnetCache;
74 private volatile INeutronPortCRUD neutronPortCache;
75 private volatile L3ForwardingProvider l3ForwardingProvider;
76 private volatile InboundNatProvider inboundNatProvider;
77 private volatile OutboundNatProvider outboundNatProvider;
78 private volatile ArpProvider arpProvider;
79 private volatile RoutingProvider routingProvider;
81 private Set<String> inboundIpRewriteCache;
82 private Set<String> outboundIpRewriteCache;
83 private Set<String> inboundIpRewriteExclusionCache;
84 private Set<String> outboundIpRewriteExclusionCache;
85 private Set<String> routerInterfacesCache;
86 private Set<String> staticArpEntryCache;
87 private Set<String> l3ForwardingCache;
88 private Set<String> defaultRouteCache;
89 private Map<String, String> networkIdToRouterMacCache;
90 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
91 private Boolean enabled = false;
94 final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
95 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
96 this.inboundIpRewriteCache = new HashSet<>();
97 this.outboundIpRewriteCache = new HashSet<>();
98 this.inboundIpRewriteExclusionCache = new HashSet<>();
99 this.outboundIpRewriteExclusionCache = new HashSet<>();
100 this.routerInterfacesCache = new HashSet<>();
101 this.staticArpEntryCache = new HashSet<>();
102 this.l3ForwardingCache = new HashSet<>();
103 this.defaultRouteCache = new HashSet<>();
104 this.networkIdToRouterMacCache = new HashMap<>();
105 this.subnetIdToRouterInterfaceCache = new HashMap<>();
108 logger.info("OVSDB L3 forwarding is enabled");
110 logger.debug("OVSDB L3 forwarding is disabled");
115 // Callbacks from OVSDB's northbound handlers
118 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
119 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
124 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
125 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
129 final boolean isDelete = action == Action.DELETE;
131 // Treat the port event as a router interface event if the port belongs to router. This is a
132 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
134 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
135 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
136 NeutronRouter_Interface neutronRouterInterface =
137 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
138 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
139 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
141 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
144 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
145 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
146 // need to do this check here because a router interface is not added to a node until tenant becomes needed
150 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
151 NeutronRouter_Interface neutronRouterInterface =
152 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
153 if (neutronRouterInterface != null) {
154 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
158 this.updateL3ForNeutronPort(neutronPort, isDelete);
162 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
163 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
168 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
169 final NeutronRouter_Interface neutronRouterInterface,
171 logger.debug("Router interface {} got event {}. Subnet {}",
172 neutronRouterInterface.getPortUUID(),
174 neutronRouterInterface.getSubnetUUID());
178 final boolean isDelete = action == Action.DELETE;
180 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
182 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
183 // see if they are affected by l3
185 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
186 boolean currPortShouldBeDeleted = false;
187 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
189 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
190 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
191 currPortShouldBeDeleted = true;
196 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
200 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
202 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
203 neutronFloatingIP.getFixedIPAddress(),
204 neutronFloatingIP.getFloatingIPAddress(),
205 neutronFloatingIP.getFloatingNetworkUUID());
209 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
212 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
213 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
219 // Callbacks from OVSDB's southbound handler
221 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
223 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
224 action, node, intf.getName(), neutronNetwork);
228 // See if there is an external uuid, so we can find the respective neutronPort
229 Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
230 if (externalIds == null) {
233 String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
234 if (neutronPortId == null) {
237 final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
238 if (neutronPort == null) {
239 logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
240 action, node, intf.getName(), neutronNetwork, neutronPortId);
243 this.handleNeutronPortEvent(neutronPort, action);
249 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
251 final String networkUUID = neutronPort.getNetworkUUID();
252 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
254 // If there is no router interface handling the networkUUID, we are done
255 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
259 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
260 // router interface are handled via handleNeutronRouterInterfaceEvent.
261 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
265 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
266 final String providerSegmentationId = neutronNetwork != null ?
267 neutronNetwork.getProviderSegmentationID() : null;
268 final String tenantMac = neutronPort.getMacAddress();
270 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
271 tenantMac == null || tenantMac.isEmpty()) {
272 return; // done: go no further w/out all the info needed...
275 final Action action = isDelete ? Action.DELETE : Action.ADD;
276 List<Node> nodes = connectionService.getNodes();
277 if (nodes.isEmpty()) {
278 logger.trace("updateL3ForNeutronPort has no nodes to work with");
280 for (Node node : nodes) {
281 final Long dpid = getDpid(node);
282 final boolean tenantNetworkPresentInNode =
283 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
284 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
285 final String tenantIpStr = neutronIP.getIpAddress();
286 if (tenantIpStr.isEmpty()) {
290 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
291 // still needed when routing to subnets non-local to node (bug 2076).
292 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
294 // Configure distributed ARP responder. Only needed if tenant network exists in node.
295 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
296 tenantNetworkPresentInNode ? action : Action.DELETE);
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.getId().getValue(), 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.getId().getValue(), 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 destNeutronRouterInterface,
363 Preconditions.checkNotNull(destNeutronRouterInterface);
365 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.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(destNeutronRouterInterface.getSubnetUUID());
369 final NeutronNetwork neutronNetwork = subnet != null ?
370 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
371 final String destinationSegmentationId = 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 destNeutronRouterInterface, isDelete);
381 if (destinationSegmentationId == null || destinationSegmentationId.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 destinationSegmentationId, 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 -- add
395 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
396 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
399 List<Node> nodes = connectionService.getNodes();
400 if (nodes.isEmpty()) {
401 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
403 for (Node node : nodes) {
404 final Long dpid = getDpid(node);
405 final Action actionForNode =
406 tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
407 action : Action.DELETE;
409 for (Neutron_IPs neutronIP : ipList) {
410 final String ipStr = neutronIP.getIpAddress();
411 if (ipStr.isEmpty()) {
412 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
413 node.getId().getValue(), ipStr);
417 // Iterate through all other interfaces and add/remove reflexive flows to this interface
419 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
420 programFlowsForNeutronRouterInterfacePair(node, dpid,
421 srcNeutronRouterInterface, destNeutronRouterInterface,
422 neutronNetwork, destinationSegmentationId,
423 macAddress, ipStr, mask, actionForNode,
424 true /*isReflexsive*/);
427 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
430 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
431 // for the external neutron networks.
434 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
435 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
436 cidr, actionForRewriteExclusion);
437 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
438 cidr, actionForRewriteExclusion);
441 // Default route. For non-external subnet, make sure that there is none configured.
443 if (gatewayIp != null && !gatewayIp.isEmpty()) {
444 final Action actionForNodeDefaultRoute =
445 isExternal ? actionForNode : Action.DELETE;
446 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
447 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
448 actionForNodeDefaultRoute);
452 // Keep cache for finding router's mac from network uuid -- remove
455 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
456 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
460 private void programFlowsForNeutronRouterInterfacePair(final Node node,
462 final NeutronRouter_Interface srcNeutronRouterInterface,
463 final NeutronRouter_Interface dstNeutronRouterInterface,
464 final NeutronNetwork dstNeutronNetwork,
465 final String destinationSegmentationId,
466 final String dstMacAddress,
467 final String destIpStr,
469 final Action actionForNode,
470 Boolean isReflexsive) {
471 Preconditions.checkNotNull(srcNeutronRouterInterface);
472 Preconditions.checkNotNull(dstNeutronRouterInterface);
474 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
475 if (sourceSubnetId == null) {
476 logger.error("Could not get provider Subnet ID from router interface {}",
477 srcNeutronRouterInterface.getID());
481 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
482 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
483 if (sourceNetworkId == null) {
484 logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
488 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
489 if (sourceNetwork == null) {
490 logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
494 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
495 // Isolate subnets from different tenants within the same router
498 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
499 if (sourceSegmentationId == null) {
500 logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
503 if (sourceSegmentationId.equals(destinationSegmentationId)) {
508 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
509 dstMacAddress, destIpStr, destMask, actionForNode);
511 // Flip roles src->dst; dst->src
513 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
514 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
515 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
516 final String cidr2 = sourceSubnet.getCidr();
517 final int mask2 = getMaskLenFromCidr(cidr2);
519 if (cidr2 == null || cidr2.isEmpty() ||
520 macAddress2 == null || macAddress2.isEmpty() ||
521 ipList2 == null || ipList2.isEmpty()) {
522 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
523 sourceSegmentationId, cidr2, macAddress2, ipList2);
524 return; // done: go no further w/out all the info needed...
527 for (Neutron_IPs neutronIP2 : ipList2) {
528 final String ipStr2 = neutronIP2.getIpAddress();
529 if (ipStr2.isEmpty()) {
532 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
533 srcNeutronRouterInterface,
534 sourceNetwork, sourceSegmentationId,
535 macAddress2, ipStr2, mask2, actionForNode,
536 false /*isReflexsive*/);
541 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
542 String destinationSegmentationId,
543 String macAddress, String ipStr, int mask,
544 Action actionForNode) {
545 // Based on the local cache, figure out whether programming needs to occur. To do this, we
546 // will look at desired action for node.
548 final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
549 ipStr + "/" + Integer.toString(mask);
550 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
552 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
553 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
554 "action {} is already done",
555 node.getId().getValue(), sourceSegmentationId, destinationSegmentationId,
556 ipStr, mask, actionForNode);
559 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
560 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
561 "action {} is already done",
562 node.getId().getValue(), sourceSegmentationId, destinationSegmentationId,
563 ipStr, mask, actionForNode);
567 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
568 macAddress, ipStr, mask, actionForNode);
569 if (status.isSuccess()) {
571 if (actionForNode == Action.ADD) {
572 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
573 routerInterfacesCache.add(cacheKey);
575 // TODO: multiTenantAwareRouter.removeInterface(...);
576 routerInterfacesCache.remove(cacheKey);
581 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
582 String destinationSegmentationId,
584 String address, int mask,
585 Action actionForNode) {
588 InetAddress inetAddress = InetAddress.getByName(address);
589 status = routingProvider == null ?
590 new Status(StatusCode.SUCCESS) :
591 routingProvider.programRouterInterface(node, dpid, sourceSegmentationId, destinationSegmentationId,
592 macAddress, inetAddress, mask, actionForNode);
593 } catch (UnknownHostException e) {
594 status = new Status(StatusCode.BADREQUEST);
597 if (status.isSuccess()) {
598 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
599 routingProvider == null ? "skipped" : "programmed",
600 macAddress, address, mask, node, sourceSegmentationId, destinationSegmentationId,
603 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
604 macAddress, address, mask, node, sourceSegmentationId, destinationSegmentationId,
605 actionForNode, status);
610 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
611 String macAddress, String ipStr,
612 Action actionForNode) {
613 // Based on the local cache, figure out whether programming needs to occur. To do this, we
614 // will look at desired action for node.
616 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
617 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
619 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
620 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
621 node.getId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
624 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
625 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
626 node.getId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
630 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
631 macAddress, ipStr, actionForNode);
632 if (status.isSuccess()) {
634 if (actionForNode == Action.ADD) {
635 staticArpEntryCache.add(cacheKey);
637 staticArpEntryCache.remove(cacheKey);
642 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
645 Action actionForNode) {
648 InetAddress inetAddress = InetAddress.getByName(address);
649 status = arpProvider == null ?
650 new Status(StatusCode.SUCCESS) :
651 arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
652 macAddress, inetAddress, actionForNode);
653 } catch (UnknownHostException e) {
654 status = new Status(StatusCode.BADREQUEST);
657 if (status.isSuccess()) {
658 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
659 arpProvider == null ? "skipped" : "programmed",
660 macAddress, address, node, actionForNode);
662 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
663 macAddress, address, node, actionForNode, status);
668 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
669 final boolean isInbound, String cidr,
670 Action actionForRewriteExclusion) {
671 // Based on the local cache, figure out whether programming needs to occur. To do this, we
672 // will look at desired action for node.
674 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
675 final Boolean isProgrammed = isInbound ?
676 inboundIpRewriteExclusionCache.contains(cacheKey):
677 outboundIpRewriteExclusionCache.contains(cacheKey);
679 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
680 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
681 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
682 actionForRewriteExclusion);
685 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
686 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
687 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
688 actionForRewriteExclusion);
692 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
693 isInbound, actionForRewriteExclusion);
694 if (status.isSuccess()) {
696 if (actionForRewriteExclusion == Action.ADD) {
698 inboundIpRewriteExclusionCache.add(cacheKey);
700 outboundIpRewriteExclusionCache.add(cacheKey);
704 inboundIpRewriteExclusionCache.remove(cacheKey);
706 outboundIpRewriteExclusionCache.remove(cacheKey);
712 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
713 final boolean isInbound, Action actionForNode) {
716 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
717 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
720 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
721 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
725 if (status.isSuccess()) {
726 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
727 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
728 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
729 cidr, node, actionForNode);
731 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
732 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
737 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
738 String defaultGatewayMacAddress, String gatewayIp,
739 Action actionForNodeDefaultRoute) {
740 // Based on the local cache, figure out whether programming needs to occur. To do this, we
741 // will look at desired action for node.
743 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
744 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
746 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
747 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
748 node.getId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
749 actionForNodeDefaultRoute);
752 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
753 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
754 node.getId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
755 actionForNodeDefaultRoute);
759 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
760 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
761 if (status.isSuccess()) {
763 if (actionForNodeDefaultRoute == Action.ADD) {
764 defaultRouteCache.add(cacheKey);
766 defaultRouteCache.remove(cacheKey);
771 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
772 String defaultGatewayMacAddress,
774 Action actionForNodeDefaultRoute) {
775 // TODO: As of Helium, mac address for default gateway is required (bug 1705).
776 if (defaultGatewayMacAddress == null) {
777 logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
778 gatewayIp, node, actionForNodeDefaultRoute);
779 return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
784 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
785 status = routingProvider == null ?
786 new Status(StatusCode.SUCCESS) :
787 routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
788 defaultGatewayMacAddress, inetAddress,
789 actionForNodeDefaultRoute);
790 } catch (UnknownHostException e) {
791 status = new Status(StatusCode.BADREQUEST);
794 if (status.isSuccess()) {
795 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
796 routingProvider == null ? "skipped" : "programmed",
797 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
799 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
800 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
805 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
806 Preconditions.checkNotNull(neutronFloatingIP);
808 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
809 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
811 // If there is no router interface handling the networkUUID, we are done
812 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
816 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
817 final String providerSegmentationId = neutronNetwork != null ?
818 neutronNetwork.getProviderSegmentationID() : null;
819 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
820 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
822 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
823 // routerMacAddress == null || routerMacAddress.isEmpty() ||
824 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
825 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
826 return; // done: go no further w/out all the info needed...
829 final Action action = isDelete ? Action.DELETE : Action.ADD;
830 List<Node> nodes = connectionService.getNodes();
831 if (nodes.isEmpty()) {
832 logger.trace("programFlowsForFloatingIP has no nodes to work with");
834 for (Node node : nodes) {
835 final Long dpid = getDpid(node);
836 final Action actionForNode =
837 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
838 action : Action.DELETE;
840 // Rewrite from float to fixed and vice-versa
842 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
843 floatingIpAddress, fixedIPAddress, actionForNode);
844 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
845 fixedIPAddress, floatingIpAddress, actionForNode);
847 // Respond to arps for the floating ip address
849 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
854 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
855 final boolean isInbound,
856 String matchAddress, String rewriteAddress,
857 Action actionForNode) {
858 // Based on the local cache, figure out whether programming needs to occur. To do this, we
859 // will look at desired action for node.
861 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
862 matchAddress + ":" + rewriteAddress;
863 final Boolean isProgrammed = isInbound ?
864 inboundIpRewriteCache.contains(cacheKey) :
865 outboundIpRewriteCache.contains(cacheKey);
867 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
868 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
870 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
871 matchAddress, rewriteAddress, actionForNode);
874 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
875 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
877 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
878 matchAddress, rewriteAddress, actionForNode);
882 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
883 matchAddress, rewriteAddress, actionForNode);
884 if (status.isSuccess()) {
886 if (actionForNode == Action.ADD) {
888 inboundIpRewriteCache.add(cacheKey);
890 outboundIpRewriteCache.add(cacheKey);
894 inboundIpRewriteCache.remove(cacheKey);
896 outboundIpRewriteCache.remove(cacheKey);
902 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
903 final boolean isInbound,
904 String matchAddress, String rewriteAddress,
905 Action actionForNode) {
908 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
909 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
911 status = inboundNatProvider == null ?
912 new Status(StatusCode.SUCCESS) :
913 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
914 inetMatchAddress, inetRewriteAddress, actionForNode);
916 status = outboundNatProvider == null ?
917 new Status(StatusCode.SUCCESS) :
918 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
919 inetMatchAddress, inetRewriteAddress, actionForNode);
921 } catch (UnknownHostException e) {
922 status = new Status(StatusCode.BADREQUEST);
925 if (status.isSuccess()) {
926 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
927 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
928 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
929 matchAddress, rewriteAddress, node, actionForNode);
931 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
932 (isInbound ? "inbound" : "outbound"),
933 matchAddress, rewriteAddress, node, actionForNode, status);
942 private int getMaskLenFromCidr(String cidr) {
943 if (cidr == null) return 0;
944 String[] splits = cidr.split("/");
945 if (splits.length != 2) return 0;
949 result = Integer.parseInt(splits[1].trim());
951 catch (NumberFormatException nfe)
958 private Long getDpid (Node node) {
959 Preconditions.checkNotNull(ovsdbConfigurationService);
961 String bridgeName = configurationService.getIntegrationBridgeName();
962 String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
963 if (bridgeUuid == null) {
964 logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
969 Row bridgeRow = ovsdbConfigurationService
970 .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
971 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
972 Set<String> dpids = bridge.getDatapathIdColumn().getData();
973 if (dpids == null || dpids.size() == 0) return 0L;
974 return StringConvertor.dpidStringToLong((String) dpids.toArray()[0]);
975 } catch (Exception e) {
976 logger.error("Error finding Bridge's OF DPID", e);
981 private String getInternalBridgeUUID (Node node, String bridgeName) {
982 Preconditions.checkNotNull(ovsdbConfigurationService);
984 Map<String, Row> bridgeTable =
985 ovsdbConfigurationService.getRows(node,
986 ovsdbConfigurationService.getTableName(node, Bridge.class));
987 if (bridgeTable == null) return null;
988 for (String key : bridgeTable.keySet()) {
989 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
990 if (bridge.getName().equals(bridgeName)) return key;
992 } catch (Exception e) {
993 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);