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.openstack.netvirt.MdsalUtils;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
25 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
29 import com.google.common.base.Preconditions;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import java.net.InetAddress;
34 import java.net.UnknownHostException;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
42 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
43 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
44 * as well as the multi-tenant router forwarding provider.
46 public class NeutronL3Adapter {
51 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
53 // The implementation for each of these services is resolved by the OSGi Service Manager
54 private volatile ConfigurationService configurationService;
55 private volatile TenantNetworkManager tenantNetworkManager;
56 private volatile OvsdbConnectionService connectionService;
57 private volatile INeutronNetworkCRUD neutronNetworkCache;
58 private volatile INeutronSubnetCRUD neutronSubnetCache;
59 private volatile INeutronPortCRUD neutronPortCache;
60 private volatile L3ForwardingProvider l3ForwardingProvider;
61 private volatile InboundNatProvider inboundNatProvider;
62 private volatile OutboundNatProvider outboundNatProvider;
63 private volatile ArpProvider arpProvider;
64 private volatile RoutingProvider routingProvider;
66 private Set<String> inboundIpRewriteCache;
67 private Set<String> outboundIpRewriteCache;
68 private Set<String> inboundIpRewriteExclusionCache;
69 private Set<String> outboundIpRewriteExclusionCache;
70 private Set<String> routerInterfacesCache;
71 private Set<String> staticArpEntryCache;
72 private Set<String> l3ForwardingCache;
73 private Set<String> defaultRouteCache;
74 private Map<String, String> networkIdToRouterMacCache;
75 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
76 private Boolean enabled = false;
79 logger.info(">>>>>> init {}", this.getClass());
80 final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
81 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
82 this.inboundIpRewriteCache = new HashSet<>();
83 this.outboundIpRewriteCache = new HashSet<>();
84 this.inboundIpRewriteExclusionCache = new HashSet<>();
85 this.outboundIpRewriteExclusionCache = new HashSet<>();
86 this.routerInterfacesCache = new HashSet<>();
87 this.staticArpEntryCache = new HashSet<>();
88 this.l3ForwardingCache = new HashSet<>();
89 this.defaultRouteCache = new HashSet<>();
90 this.networkIdToRouterMacCache = new HashMap<>();
91 this.subnetIdToRouterInterfaceCache = new HashMap<>();
94 logger.info("OVSDB L3 forwarding is enabled");
96 logger.debug("OVSDB L3 forwarding is disabled");
101 // Callbacks from OVSDB's northbound handlers
104 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
105 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
110 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
111 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
115 final boolean isDelete = action == Action.DELETE;
117 // Treat the port event as a router interface event if the port belongs to router. This is a
118 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
120 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
121 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
122 NeutronRouter_Interface neutronRouterInterface =
123 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
124 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
125 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
127 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
130 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
131 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
132 // need to do this check here because a router interface is not added to a node until tenant becomes needed
136 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
137 NeutronRouter_Interface neutronRouterInterface =
138 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
139 if (neutronRouterInterface != null) {
140 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
144 this.updateL3ForNeutronPort(neutronPort, isDelete);
148 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
149 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
154 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
155 final NeutronRouter_Interface neutronRouterInterface,
157 logger.debug("Router interface {} got event {}. Subnet {}",
158 neutronRouterInterface.getPortUUID(),
160 neutronRouterInterface.getSubnetUUID());
164 final boolean isDelete = action == Action.DELETE;
166 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
168 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
169 // see if they are affected by l3
171 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
172 boolean currPortShouldBeDeleted = false;
173 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
175 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
176 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
177 currPortShouldBeDeleted = true;
182 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
186 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
188 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
189 neutronFloatingIP.getFixedIPAddress(),
190 neutronFloatingIP.getFloatingIPAddress(),
191 neutronFloatingIP.getFloatingNetworkUUID());
195 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
198 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
199 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
205 // Callbacks from OVSDB's southbound handler
207 public void handleInterfaceEvent(final Node node, final OvsdbTerminationPointAugmentation intf,
208 final NeutronNetwork neutronNetwork, Action action) {
209 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
210 action, node, intf.getName(), neutronNetwork);
214 NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
215 if (neutronPort != null) {
216 this.handleNeutronPortEvent(neutronPort, action);
223 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
225 final String networkUUID = neutronPort.getNetworkUUID();
226 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
228 // If there is no router interface handling the networkUUID, we are done
229 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
233 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
234 // router interface are handled via handleNeutronRouterInterfaceEvent.
235 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
239 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
240 final String providerSegmentationId = neutronNetwork != null ?
241 neutronNetwork.getProviderSegmentationID() : null;
242 final String tenantMac = neutronPort.getMacAddress();
244 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
245 tenantMac == null || tenantMac.isEmpty()) {
246 return; // done: go no further w/out all the info needed...
249 final Action action = isDelete ? Action.DELETE : Action.ADD;
250 List<Node> nodes = connectionService.getBridgeNodes();
251 if (nodes.isEmpty()) {
252 logger.trace("updateL3ForNeutronPort has no nodes to work with");
254 for (Node node : nodes) {
255 final Long dpid = getDpid(node);
256 final boolean tenantNetworkPresentInNode =
257 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
258 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
259 final String tenantIpStr = neutronIP.getIpAddress();
260 if (tenantIpStr.isEmpty()) {
264 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
265 // still needed when routing to subnets non-local to node (bug 2076).
266 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
268 // Configure distributed ARP responder. Only needed if tenant network exists in node.
269 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
270 tenantNetworkPresentInNode ? action : Action.DELETE);
275 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
276 String macAddress, String ipStr,
277 Action actionForNode) {
278 // Based on the local cache, figure out whether programming needs to occur. To do this, we
279 // will look at desired action for node.
281 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
282 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
284 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
285 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
286 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
289 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
290 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
291 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
295 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
296 macAddress, ipStr, actionForNode);
297 if (status.isSuccess()) {
299 if (actionForNode == Action.ADD) {
300 l3ForwardingCache.add(cacheKey);
302 l3ForwardingCache.remove(cacheKey);
307 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
310 Action actionForNode) {
313 InetAddress inetAddress = InetAddress.getByName(address);
314 status = l3ForwardingProvider == null ?
315 new Status(StatusCode.SUCCESS) :
316 l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
317 inetAddress, macAddress, actionForNode);
318 } catch (UnknownHostException e) {
319 status = new Status(StatusCode.BADREQUEST);
322 if (status.isSuccess()) {
323 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
324 l3ForwardingProvider == null ? "skipped" : "programmed",
325 macAddress, address, node, actionForNode);
327 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
328 macAddress, address, node, actionForNode, status);
335 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
337 Preconditions.checkNotNull(destNeutronRouterInterface);
339 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
340 final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
341 final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
342 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
343 final NeutronNetwork neutronNetwork = subnet != null ?
344 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
345 final String destinationSegmentationId = neutronNetwork != null ?
346 neutronNetwork.getProviderSegmentationID() : null;
347 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
348 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
349 final String cidr = subnet != null ? subnet.getCidr() : null;
350 final int mask = getMaskLenFromCidr(cidr);
352 logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
353 destNeutronRouterInterface, isDelete);
355 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
356 cidr == null || cidr.isEmpty() ||
357 macAddress == null || macAddress.isEmpty() ||
358 ipList == null || ipList.isEmpty()) {
359 logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
360 destinationSegmentationId, cidr, macAddress, ipList);
361 return; // done: go no further w/out all the info needed...
364 final Action action = isDelete ? Action.DELETE : Action.ADD;
366 // Keep cache for finding router's mac from network uuid -- add
369 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
370 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
373 List<Node> nodes = connectionService.getBridgeNodes();
374 if (nodes.isEmpty()) {
375 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
377 for (Node node : nodes) {
378 final Long dpid = getDpid(node);
379 final Action actionForNode =
380 tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
381 action : Action.DELETE;
383 for (Neutron_IPs neutronIP : ipList) {
384 final String ipStr = neutronIP.getIpAddress();
385 if (ipStr.isEmpty()) {
386 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
387 node.getNodeId().getValue(), ipStr);
391 // Iterate through all other interfaces and add/remove reflexive flows to this interface
393 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
394 programFlowsForNeutronRouterInterfacePair(node, dpid,
395 srcNeutronRouterInterface, destNeutronRouterInterface,
396 neutronNetwork, destinationSegmentationId,
397 macAddress, ipStr, mask, actionForNode,
398 true /*isReflexsive*/);
401 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
404 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
405 // for the external neutron networks.
408 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
409 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
410 cidr, actionForRewriteExclusion);
411 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
412 cidr, actionForRewriteExclusion);
415 // Default route. For non-external subnet, make sure that there is none configured.
417 if (gatewayIp != null && !gatewayIp.isEmpty()) {
418 final Action actionForNodeDefaultRoute =
419 isExternal ? actionForNode : Action.DELETE;
420 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
421 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
422 actionForNodeDefaultRoute);
426 // Keep cache for finding router's mac from network uuid -- remove
429 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
430 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
434 private void programFlowsForNeutronRouterInterfacePair(final Node node,
436 final NeutronRouter_Interface srcNeutronRouterInterface,
437 final NeutronRouter_Interface dstNeutronRouterInterface,
438 final NeutronNetwork dstNeutronNetwork,
439 final String destinationSegmentationId,
440 final String dstMacAddress,
441 final String destIpStr,
443 final Action actionForNode,
444 Boolean isReflexsive) {
445 Preconditions.checkNotNull(srcNeutronRouterInterface);
446 Preconditions.checkNotNull(dstNeutronRouterInterface);
448 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
449 if (sourceSubnetId == null) {
450 logger.error("Could not get provider Subnet ID from router interface {}",
451 srcNeutronRouterInterface.getID());
455 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
456 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
457 if (sourceNetworkId == null) {
458 logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
462 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
463 if (sourceNetwork == null) {
464 logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
468 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
469 // Isolate subnets from different tenants within the same router
472 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
473 if (sourceSegmentationId == null) {
474 logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
477 if (sourceSegmentationId.equals(destinationSegmentationId)) {
482 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
483 dstMacAddress, destIpStr, destMask, actionForNode);
485 // Flip roles src->dst; dst->src
487 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
488 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
489 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
490 final String cidr2 = sourceSubnet.getCidr();
491 final int mask2 = getMaskLenFromCidr(cidr2);
493 if (cidr2 == null || cidr2.isEmpty() ||
494 macAddress2 == null || macAddress2.isEmpty() ||
495 ipList2 == null || ipList2.isEmpty()) {
496 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
497 sourceSegmentationId, cidr2, macAddress2, ipList2);
498 return; // done: go no further w/out all the info needed...
501 for (Neutron_IPs neutronIP2 : ipList2) {
502 final String ipStr2 = neutronIP2.getIpAddress();
503 if (ipStr2.isEmpty()) {
506 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
507 srcNeutronRouterInterface,
508 sourceNetwork, sourceSegmentationId,
509 macAddress2, ipStr2, mask2, actionForNode,
510 false /*isReflexsive*/);
515 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
516 String destinationSegmentationId,
517 String macAddress, String ipStr, int mask,
518 Action actionForNode) {
519 // Based on the local cache, figure out whether programming needs to occur. To do this, we
520 // will look at desired action for node.
522 final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
523 ipStr + "/" + Integer.toString(mask);
524 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
526 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
527 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
528 "action {} is already done",
529 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
530 ipStr, mask, actionForNode);
533 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
534 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
535 "action {} is already done",
536 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
537 ipStr, mask, actionForNode);
541 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
542 macAddress, ipStr, mask, actionForNode);
543 if (status.isSuccess()) {
545 if (actionForNode == Action.ADD) {
546 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
547 routerInterfacesCache.add(cacheKey);
549 // TODO: multiTenantAwareRouter.removeInterface(...);
550 routerInterfacesCache.remove(cacheKey);
555 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
556 String destinationSegmentationId,
558 String address, int mask,
559 Action actionForNode) {
562 InetAddress inetAddress = InetAddress.getByName(address);
563 status = routingProvider == null ?
564 new Status(StatusCode.SUCCESS) :
565 routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
566 macAddress, inetAddress, mask, actionForNode);
567 } catch (UnknownHostException e) {
568 status = new Status(StatusCode.BADREQUEST);
571 if (status.isSuccess()) {
572 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
573 routingProvider == null ? "skipped" : "programmed",
574 macAddress, address, mask, node, sourceSegmentationId, destinationSegmentationId,
577 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
578 macAddress, address, mask, node, sourceSegmentationId, destinationSegmentationId,
579 actionForNode, status);
584 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
585 String macAddress, String ipStr,
586 Action actionForNode) {
587 // Based on the local cache, figure out whether programming needs to occur. To do this, we
588 // will look at desired action for node.
590 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
591 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
593 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
594 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
595 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
598 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
599 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
600 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
604 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
605 macAddress, ipStr, actionForNode);
606 if (status.isSuccess()) {
608 if (actionForNode == Action.ADD) {
609 staticArpEntryCache.add(cacheKey);
611 staticArpEntryCache.remove(cacheKey);
616 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
619 Action actionForNode) {
622 InetAddress inetAddress = InetAddress.getByName(address);
623 status = arpProvider == null ?
624 new Status(StatusCode.SUCCESS) :
625 arpProvider.programStaticArpEntry(dpid, providerSegmentationId,
626 macAddress, inetAddress, actionForNode);
627 } catch (UnknownHostException e) {
628 status = new Status(StatusCode.BADREQUEST);
631 if (status.isSuccess()) {
632 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
633 arpProvider == null ? "skipped" : "programmed",
634 macAddress, address, node, actionForNode);
636 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
637 macAddress, address, node, actionForNode, status);
642 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
643 final boolean isInbound, String cidr,
644 Action actionForRewriteExclusion) {
645 // Based on the local cache, figure out whether programming needs to occur. To do this, we
646 // will look at desired action for node.
648 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
649 final Boolean isProgrammed = isInbound ?
650 inboundIpRewriteExclusionCache.contains(cacheKey):
651 outboundIpRewriteExclusionCache.contains(cacheKey);
653 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
654 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
655 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
656 actionForRewriteExclusion);
659 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
660 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
661 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
662 actionForRewriteExclusion);
666 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
667 isInbound, actionForRewriteExclusion);
668 if (status.isSuccess()) {
670 if (actionForRewriteExclusion == Action.ADD) {
672 inboundIpRewriteExclusionCache.add(cacheKey);
674 outboundIpRewriteExclusionCache.add(cacheKey);
678 inboundIpRewriteExclusionCache.remove(cacheKey);
680 outboundIpRewriteExclusionCache.remove(cacheKey);
686 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
687 final boolean isInbound, Action actionForNode) {
690 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
691 inboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr,
694 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
695 outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr,
699 if (status.isSuccess()) {
700 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
701 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
702 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
703 cidr, node, actionForNode);
705 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
706 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
711 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
712 String defaultGatewayMacAddress, String gatewayIp,
713 Action actionForNodeDefaultRoute) {
714 // Based on the local cache, figure out whether programming needs to occur. To do this, we
715 // will look at desired action for node.
717 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
718 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
720 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
721 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
722 node.getNodeId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
723 actionForNodeDefaultRoute);
726 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
727 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
728 node.getNodeId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
729 actionForNodeDefaultRoute);
733 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
734 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
735 if (status.isSuccess()) {
737 if (actionForNodeDefaultRoute == Action.ADD) {
738 defaultRouteCache.add(cacheKey);
740 defaultRouteCache.remove(cacheKey);
745 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
746 String defaultGatewayMacAddress,
748 Action actionForNodeDefaultRoute) {
749 // TODO: As of Helium, mac address for default gateway is required (bug 1705).
750 if (defaultGatewayMacAddress == null) {
751 logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
752 gatewayIp, node, actionForNodeDefaultRoute);
753 return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
758 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
759 status = routingProvider == null ?
760 new Status(StatusCode.SUCCESS) :
761 routingProvider.programDefaultRouteEntry(dpid, providerSegmentationId,
762 defaultGatewayMacAddress, inetAddress,
763 actionForNodeDefaultRoute);
764 } catch (UnknownHostException e) {
765 status = new Status(StatusCode.BADREQUEST);
768 if (status.isSuccess()) {
769 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
770 routingProvider == null ? "skipped" : "programmed",
771 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
773 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
774 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
779 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
780 Preconditions.checkNotNull(neutronFloatingIP);
782 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
783 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
785 // If there is no router interface handling the networkUUID, we are done
786 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
790 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
791 final String providerSegmentationId = neutronNetwork != null ?
792 neutronNetwork.getProviderSegmentationID() : null;
793 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
794 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
796 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
797 // routerMacAddress == null || routerMacAddress.isEmpty() ||
798 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
799 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
800 return; // done: go no further w/out all the info needed...
803 final Action action = isDelete ? Action.DELETE : Action.ADD;
804 List<Node> nodes = connectionService.getBridgeNodes();
805 if (nodes.isEmpty()) {
806 logger.trace("programFlowsForFloatingIP has no nodes to work with");
808 for (Node node : nodes) {
809 final Long dpid = getDpid(node);
810 final Action actionForNode =
811 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
812 action : Action.DELETE;
814 // Rewrite from float to fixed and vice-versa
816 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
817 floatingIpAddress, fixedIPAddress, actionForNode);
818 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
819 fixedIPAddress, floatingIpAddress, actionForNode);
821 // Respond to arps for the floating ip address
823 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
828 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
829 final boolean isInbound,
830 String matchAddress, String rewriteAddress,
831 Action actionForNode) {
832 // Based on the local cache, figure out whether programming needs to occur. To do this, we
833 // will look at desired action for node.
835 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
836 matchAddress + ":" + rewriteAddress;
837 final Boolean isProgrammed = isInbound ?
838 inboundIpRewriteCache.contains(cacheKey) :
839 outboundIpRewriteCache.contains(cacheKey);
841 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
842 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
844 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
845 matchAddress, rewriteAddress, actionForNode);
848 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
849 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
851 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
852 matchAddress, rewriteAddress, actionForNode);
856 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
857 matchAddress, rewriteAddress, actionForNode);
858 if (status.isSuccess()) {
860 if (actionForNode == Action.ADD) {
862 inboundIpRewriteCache.add(cacheKey);
864 outboundIpRewriteCache.add(cacheKey);
868 inboundIpRewriteCache.remove(cacheKey);
870 outboundIpRewriteCache.remove(cacheKey);
876 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
877 final boolean isInbound,
878 String matchAddress, String rewriteAddress,
879 Action actionForNode) {
882 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
883 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
885 status = inboundNatProvider == null ?
886 new Status(StatusCode.SUCCESS) :
887 inboundNatProvider.programIpRewriteRule(dpid, providerSegmentationId,
888 inetMatchAddress, inetRewriteAddress, actionForNode);
890 status = outboundNatProvider == null ?
891 new Status(StatusCode.SUCCESS) :
892 outboundNatProvider.programIpRewriteRule(dpid, providerSegmentationId,
893 inetMatchAddress, inetRewriteAddress, actionForNode);
895 } catch (UnknownHostException e) {
896 status = new Status(StatusCode.BADREQUEST);
899 if (status.isSuccess()) {
900 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
901 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
902 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
903 matchAddress, rewriteAddress, node, actionForNode);
905 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
906 (isInbound ? "inbound" : "outbound"),
907 matchAddress, rewriteAddress, node, actionForNode, status);
912 private int getMaskLenFromCidr(String cidr) {
913 if (cidr == null) return 0;
914 String[] splits = cidr.split("/");
915 if (splits.length != 2) return 0;
919 result = Integer.parseInt(splits[1].trim());
921 catch (NumberFormatException nfe)
928 private Long getDpid(Node node) {
929 /* TODO SB_MIGRATION */
930 // may need to go from OvsdbNode to BridgeNode
931 // get integration bridge on this node and then get dpid
932 return MdsalUtils.getDataPathId(node);