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
10 package org.opendaylight.ovsdb.openstack.netvirt.impl;
12 import org.apache.commons.lang3.tuple.ImmutablePair;
13 import org.apache.commons.lang3.tuple.Pair;
14 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
15 import org.opendaylight.neutron.spi.INeutronPortCRUD;
16 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
17 import org.opendaylight.neutron.spi.NeutronFloatingIP;
18 import org.opendaylight.neutron.spi.NeutronNetwork;
19 import org.opendaylight.neutron.spi.NeutronPort;
20 import org.opendaylight.neutron.spi.NeutronRouter;
21 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
22 import org.opendaylight.neutron.spi.NeutronSubnet;
23 import org.opendaylight.neutron.spi.Neutron_IPs;
24 import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
25 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
26 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
33 import com.google.common.base.Preconditions;
34 import com.google.common.util.concurrent.FutureCallback;
35 import com.google.common.util.concurrent.Futures;
36 import com.google.common.util.concurrent.ListenableFuture;
38 import org.osgi.framework.BundleContext;
39 import org.osgi.framework.ServiceReference;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import java.net.InetAddress;
44 import java.net.UnknownHostException;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.ArrayList;
48 import java.util.List;
51 import java.util.concurrent.ExecutorService;
52 import java.util.concurrent.Executors;
55 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
56 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
57 * as well as the multi-tenant router forwarding provider.
59 public class NeutronL3Adapter implements ConfigInterface {
60 private static final Logger LOG = LoggerFactory.getLogger(NeutronL3Adapter.class);
62 // The implementation for each of these services is resolved by the OSGi Service Manager
63 private volatile ConfigurationService configurationService;
64 private volatile TenantNetworkManager tenantNetworkManager;
65 private volatile NodeCacheManager nodeCacheManager;
66 private volatile INeutronNetworkCRUD neutronNetworkCache;
67 private volatile INeutronSubnetCRUD neutronSubnetCache;
68 private volatile INeutronPortCRUD neutronPortCache;
69 private volatile L3ForwardingProvider l3ForwardingProvider;
70 private volatile InboundNatProvider inboundNatProvider;
71 private volatile OutboundNatProvider outboundNatProvider;
72 private volatile ArpProvider arpProvider;
73 private volatile RoutingProvider routingProvider;
74 private volatile GatewayMacResolver gatewayMacResolver;
76 private class FloatIpData {
77 private final Long dpid; // br-int of node where floating ip is associated with tenant port
78 private final Long ofPort; // patch port in br-int used to reach br-ex
79 private final String segId; // segmentation id of the net where fixed ip is instantiated
80 private final String macAddress; // mac address assigned to neutron port of floating ip
81 private final String floatingIpAddress;
82 private final String fixedIpAddress; // ip address given to tenant vm
83 private final String neutronRouterMac;
85 FloatIpData(final Long dpid, final Long ofPort, final String segId, final String macAddress,
86 final String floatingIpAddress, final String fixedIpAddress, final String neutronRouterMac) {
90 this.macAddress = macAddress;
91 this.floatingIpAddress = floatingIpAddress;
92 this.fixedIpAddress = fixedIpAddress;
93 this.neutronRouterMac = neutronRouterMac;
97 private Set<String> inboundIpRewriteCache;
98 private Set<String> outboundIpRewriteCache;
99 private Set<String> outboundIpRewriteExclusionCache;
100 private Set<String> routerInterfacesCache;
101 private Set<String> staticArpEntryCache;
102 private Set<String> l3ForwardingCache;
103 private Map<String, String> networkIdToRouterMacCache;
104 private Map<String, List<Neutron_IPs>> networkIdToRouterIpListCache;
105 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
106 private Map<String, Pair<Long, Uuid>> neutronPortToDpIdCache;
107 private Map<String, FloatIpData> floatIpDataMapCache;
108 private String externalRouterMac;
109 private Boolean enabled = false;
110 private Boolean flgDistributedARPEnabled = true;
111 private Southbound southbound;
112 private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5);
114 private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
115 private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
116 private static final String OWNER_ROUTER_GATEWAY = "network:router_gateway";
117 private static final String OWNER_FLOATING_IP = "network:floatingip";
118 private static final String DEFAULT_EXT_RTR_MAC = "00:00:5E:00:01:01";
120 public NeutronL3Adapter() {
121 LOG.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass());
124 private void initL3AdapterMembers() {
125 Preconditions.checkNotNull(configurationService);
127 if (configurationService.isL3ForwardingEnabled()) {
128 this.inboundIpRewriteCache = new HashSet<>();
129 this.outboundIpRewriteCache = new HashSet<>();
130 this.outboundIpRewriteExclusionCache = new HashSet<>();
131 this.routerInterfacesCache = new HashSet<>();
132 this.staticArpEntryCache = new HashSet<>();
133 this.l3ForwardingCache = new HashSet<>();
134 this.networkIdToRouterMacCache = new HashMap<>();
135 this.networkIdToRouterIpListCache = new HashMap<>();
136 this.subnetIdToRouterInterfaceCache = new HashMap<>();
137 this.neutronPortToDpIdCache = new HashMap<>();
138 this.floatIpDataMapCache = new HashMap<>();
140 this.externalRouterMac = configurationService.getDefaultGatewayMacAddress(null);
141 if (this.externalRouterMac == null) {
142 this.externalRouterMac = DEFAULT_EXT_RTR_MAC;
146 LOG.info("OVSDB L3 forwarding is enabled");
147 if (configurationService.isDistributedArpDisabled()) {
148 this.flgDistributedARPEnabled = false;
149 LOG.info("Distributed ARP responder is disabled");
151 LOG.debug("Distributed ARP responder is enabled");
154 LOG.debug("OVSDB L3 forwarding is disabled");
159 // Callbacks from OVSDB's northbound handlers
163 * Invoked to configure the mac address for the external gateway in br-ex. ovsdb netvirt needs help in getting
164 * mac for given ip in br-ex (bug 3378). For example, since ovsdb has no real arp, it needs a service in can
165 * subscribe so that the mac address associated to the gateway ip address is available.
167 * @param externalRouterMacUpdate The mac address to be associated to the gateway.
169 public void updateExternalRouterMac(final String externalRouterMacUpdate) {
170 Preconditions.checkNotNull(externalRouterMacUpdate);
172 flushExistingIpRewrite();
173 this.externalRouterMac = externalRouterMacUpdate;
174 rebuildExistingIpRewrite();
180 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
181 * @param subnet An instance of NeutronSubnet object.
183 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
184 LOG.debug("Neutron subnet {} event : {}", action, subnet.toString());
191 * Process the port event as a router interface event.
192 * For a not delete action, since a port is only create when the tennat uses the subnet, it is required to
193 * verify if all routers across all nodes have the interface for the port's subnet(s) configured.
195 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
196 * @param neutronPort An instance of NeutronPort object.
198 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
199 LOG.debug("Neutron port {} event : {}", action, neutronPort.toString());
204 final boolean isDelete = action == Action.DELETE;
206 if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
208 Node externalBridgeNode = getExternalBridgeNode();
209 if(externalBridgeNode != null){
210 LOG.info("Port {} is network router gateway interface, "
211 + "triggering gateway resolution for the attached external network on node {}", neutronPort, externalBridgeNode);
212 this.triggerGatewayMacResolver(externalBridgeNode, neutronPort);
214 LOG.error("Did not find Node that has external bridge (br-ex), Gateway resolution failed");
217 NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
219 if(externalNetwork != null){
220 if(externalNetwork.isRouterExternal()){
221 final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
222 if(externalSubnet != null){
223 gatewayMacResolver.stopPeriodicReferesh(new Ipv4Address(externalSubnet.getGatewayIP()));
230 // Treat the port event as a router interface event if the port belongs to router. This is a
231 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
233 if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
234 neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
236 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
237 NeutronRouter_Interface neutronRouterInterface =
238 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
239 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
240 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
242 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
245 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
246 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
247 // need to do this check here because a router interface is not added to a node until tenant becomes needed
251 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
252 NeutronRouter_Interface neutronRouterInterface =
253 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
254 if (neutronRouterInterface != null) {
255 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
259 this.updateL3ForNeutronPort(neutronPort, isDelete);
266 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
267 * @param neutronRouter An instance of NeutronRouter object.
269 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
270 LOG.debug("Neutron router {} event : {}", action, neutronRouter.toString());
277 * Process the event enforcing actions and verifying dependencies between all router's interface. For example,
278 * delete the ports on the same subnet.
280 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
281 * @param neutronRouter An instance of NeutronRouter object.
282 * @param neutronRouterInterface An instance of NeutronRouter_Interface object.
284 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
285 final NeutronRouter_Interface neutronRouterInterface,
287 LOG.debug("Router interface {} got event {}. Subnet {}",
288 neutronRouterInterface.getPortUUID(),
290 neutronRouterInterface.getSubnetUUID());
295 final boolean isDelete = action == Action.DELETE;
297 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
299 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
300 // see if they are affected by l3
302 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
303 boolean currPortShouldBeDeleted = false;
304 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
306 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
307 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
308 currPortShouldBeDeleted = true;
313 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
318 * Invoked when a neutron message regarding the floating ip association is sent to odl via ml2. If the action is
319 * a creation, it will first add ARP rules for the given floating ip and then configure the DNAT (rewrite the
320 * packets from the floating IP address to the internal fixed ip) rules on OpenFlow Table 30 and SNAT rules (other
321 * way around) on OpenFlow Table 100.
323 * @param actionIn the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
324 * @param neutronFloatingIP An {@link org.opendaylight.neutron.spi.NeutronFloatingIP} instance of NeutronFloatingIP object.
326 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
328 Preconditions.checkNotNull(neutronFloatingIP);
330 LOG.debug(" Floating IP {} {}<->{}, network uuid {}", actionIn,
331 neutronFloatingIP.getFixedIPAddress(),
332 neutronFloatingIP.getFloatingIPAddress(),
333 neutronFloatingIP.getFloatingNetworkUUID());
340 // Consider action to be delete if getFixedIPAddress is null
342 if (neutronFloatingIP.getFixedIPAddress() == null) {
343 action = Action.DELETE;
348 // this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
350 if (action != Action.DELETE) {
351 programFlowsForFloatingIPArpAdd(neutronFloatingIP); // must be first, as it updates floatIpDataMapCache
353 programFlowsForFloatingIPInbound(neutronFloatingIP, Action.ADD);
354 programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.ADD);
356 programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.DELETE);
357 programFlowsForFloatingIPInbound(neutronFloatingIP, Action.DELETE);
359 programFlowsForFloatingIPArpDelete(neutronFloatingIP.getID()); // must be last, as it updates floatIpDataMapCache
364 * This method performs creation or deletion of in-bound rules into Table 30 for a existing available floating
365 * ip, otherwise for newer one.
367 private void programFlowsForFloatingIPInbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
368 Preconditions.checkNotNull(neutronFloatingIP);
370 final FloatIpData fid = floatIpDataMapCache.get(neutronFloatingIP.getID());
372 LOG.trace("programFlowsForFloatingIPInboundAdd {} for {} uuid {} not in local cache",
373 action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
376 programInboundIpRewriteStage1(fid.dpid, fid.ofPort, fid.segId, fid.floatingIpAddress, fid.fixedIpAddress,
381 * This method performs creation or deletion of out-bound rules into Table 100 for a existing available floating
382 * ip, otherwise for newer one.
384 private void programFlowsForFloatingIPOutbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
385 Preconditions.checkNotNull(neutronFloatingIP);
387 final FloatIpData fid = floatIpDataMapCache.get(neutronFloatingIP.getID());
389 LOG.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache",
390 action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
393 programOutboundIpRewriteStage1(fid, action);
396 private void flushExistingIpRewrite() {
397 for (FloatIpData fid : floatIpDataMapCache.values()) {
398 programOutboundIpRewriteStage1(fid, Action.DELETE);
402 private void rebuildExistingIpRewrite() {
403 for (FloatIpData fid : floatIpDataMapCache.values()) {
404 programOutboundIpRewriteStage1(fid, Action.ADD);
409 * This method creates ARP response rules into OpenFlow Table 30 for a given floating ip. In order to connect
410 * to br-ex from br-int, a patch-port is used. Thus, the patch-port will be responsible to respond the ARP
413 private void programFlowsForFloatingIPArpAdd(final NeutronFloatingIP neutronFloatingIP) {
414 Preconditions.checkNotNull(neutronFloatingIP);
415 Preconditions.checkNotNull(neutronFloatingIP.getFixedIPAddress());
416 Preconditions.checkNotNull(neutronFloatingIP.getFloatingIPAddress());
418 if (floatIpDataMapCache.get(neutronFloatingIP.getID()) != null) {
419 LOG.trace("programFlowsForFloatingIPArpAdd for neutronFloatingIP {} uuid {} is already done",
420 neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
424 // find bridge Node where floating ip is configured by looking up cache for its port
425 final NeutronPort neutronPortForFloatIp = findNeutronPortForFloatingIp(neutronFloatingIP.getID());
426 final String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
427 final Pair<Long, Uuid> nodeIfPair = neutronPortToDpIdCache.get(neutronTenantPortUuid);
428 final String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
429 final String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
430 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
432 final NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
433 final NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
434 neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
435 final String providerSegmentationId = tenantNeutronNetwork != null ?
436 tenantNeutronNetwork.getProviderSegmentationID() : null;
437 final String neutronRouterMac = tenantNeutronNetwork != null ?
438 networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
440 if (nodeIfPair == null || neutronTenantPortUuid == null ||
441 providerSegmentationId == null || providerSegmentationId.isEmpty() ||
442 floatingIpMac == null || floatingIpMac.isEmpty() ||
443 neutronRouterMac == null || neutronRouterMac.isEmpty()) {
444 LOG.trace("Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} seg {} mac {} rtrMac {}",
447 neutronPortForFloatIp,
448 neutronTenantPortUuid,
449 providerSegmentationId,
455 // get ofport for patch port in br-int
456 final Long dpId = nodeIfPair.getLeft();
457 final Long ofPort = findOFPortForExtPatch(dpId);
458 if (ofPort == null) {
459 LOG.warn("Unable to locate OF port of patch port to connect floating ip to external bridge. dpid {}",
464 // Respond to ARPs for the floating ip address by default, via the patch port that connects br-int to br-ex
466 if (programStaticArpStage1(dpId, encodeExcplicitOFPort(ofPort), floatingIpMac, floatingIpAddress,
468 final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
469 floatingIpAddress, fixedIpAddress, neutronRouterMac);
470 floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
471 LOG.info("Floating IP {}<->{} programmed ARP mac {} on OFport {} seg {} dpid {}",
472 neutronFloatingIP.getFixedIPAddress(), neutronFloatingIP.getFloatingIPAddress(),
473 floatingIpMac, ofPort, providerSegmentationId, dpId);
477 private void programFlowsForFloatingIPArpDelete(final String neutronFloatingIPUuid) {
478 final FloatIpData floatIpData = floatIpDataMapCache.get(neutronFloatingIPUuid);
479 if (floatIpData == null) {
480 LOG.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid);
484 if (programStaticArpStage1(floatIpData.dpid, encodeExcplicitOFPort(floatIpData.ofPort), floatIpData.macAddress,
485 floatIpData.floatingIpAddress, Action.DELETE)) {
486 floatIpDataMapCache.remove(neutronFloatingIPUuid);
487 LOG.info("Floating IP {} un-programmed ARP mac {} on {} dpid {}",
488 floatIpData.floatingIpAddress, floatIpData.macAddress, floatIpData.ofPort, floatIpData.dpid);
492 private final NeutronPort findNeutronPortForFloatingIp(final String floatingIpUuid) {
493 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
494 if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
495 neutronPort.getDeviceID().equals(floatingIpUuid)) {
502 private final Long findOFPortForExtPatch(Long dpId) {
503 final String brInt = configurationService.getIntegrationBridgeName();
504 final String brExt = configurationService.getExternalBridgeName();
505 final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
507 Preconditions.checkNotNull(dpId);
508 Preconditions.checkNotNull(portNameInt);
510 final long dpidPrimitive = dpId.longValue();
511 for (Node node : nodeCacheManager.getBridgeNodes()) {
512 if (dpidPrimitive == southbound.getDataPathId(node)) {
513 final OvsdbTerminationPointAugmentation terminationPointOfBridge =
514 southbound.getTerminationPointOfBridge(node, portNameInt);
515 return terminationPointOfBridge == null ? null : terminationPointOfBridge.getOfport();
524 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
525 * @param neutronNetwork An {@link org.opendaylight.neutron.spi.NeutronNetwork} instance of NeutronFloatingIP object.
527 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
528 LOG.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
535 // Callbacks from OVSDB's southbound handler
540 * @param bridgeNode An instance of Node object.
541 * @param intf An {@link org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105
542 * .OvsdbTerminationPointAugmentation} instance of OvsdbTerminationPointAugmentation object.
543 * @param neutronNetwork An {@link org.opendaylight.neutron.spi.NeutronNetwork} instance of NeutronNetwork
545 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
547 public void handleInterfaceEvent(final Node bridgeNode, final OvsdbTerminationPointAugmentation intf,
548 final NeutronNetwork neutronNetwork, Action action) {
549 LOG.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
550 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork);
555 final NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
556 final Long dpId = getDpidForIntegrationBridge(bridgeNode);
557 final Uuid interfaceUuid = intf.getInterfaceUuid();
559 LOG.trace("southbound interface {} node:{} interface:{}, neutronNetwork:{} port:{} dpid:{} intfUuid:{}",
560 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork, neutronPort, dpId, interfaceUuid);
562 if (neutronPort != null) {
563 final String neutronPortUuid = neutronPort.getPortUUID();
565 if (action != Action.DELETE && neutronPortToDpIdCache.get(neutronPortUuid) == null &&
566 dpId != null && interfaceUuid != null) {
567 handleInterfaceEventAdd(neutronPortUuid, dpId, interfaceUuid);
570 handleNeutronPortEvent(neutronPort, action);
573 if (action == Action.DELETE && interfaceUuid != null) {
574 handleInterfaceEventDelete(intf, dpId);
578 private void handleInterfaceEventAdd(final String neutronPortUuid, Long dpId, final Uuid interfaceUuid) {
579 neutronPortToDpIdCache.put(neutronPortUuid, new ImmutablePair<>(dpId, interfaceUuid));
580 LOG.debug("handleInterfaceEvent add cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
581 neutronPortUuid, dpId, interfaceUuid.getValue());
584 private void handleInterfaceEventDelete(final OvsdbTerminationPointAugmentation intf, final Long dpId) {
585 // Remove entry from neutronPortToDpIdCache based on interface uuid
586 for (Map.Entry<String, Pair<Long, Uuid>> entry : neutronPortToDpIdCache.entrySet()) {
587 final String currPortUuid = entry.getKey();
588 if (intf.getInterfaceUuid().equals(entry.getValue().getRight())) {
589 LOG.debug("handleInterfaceEventDelete remove cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
590 currPortUuid, dpId, intf.getInterfaceUuid().getValue());
591 neutronPortToDpIdCache.remove(currPortUuid);
600 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
602 final String networkUUID = neutronPort.getNetworkUUID();
603 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
605 // If there is no router interface handling the networkUUID, we are done
606 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
610 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
611 // router interface are handled via handleNeutronRouterInterfaceEvent.
612 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
616 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
617 final String providerSegmentationId = neutronNetwork != null ?
618 neutronNetwork.getProviderSegmentationID() : null;
619 final String tenantMac = neutronPort.getMacAddress();
621 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
622 tenantMac == null || tenantMac.isEmpty()) {
623 // done: go no further w/out all the info needed...
627 final Action action = isDelete ? Action.DELETE : Action.ADD;
628 List<Node> nodes = nodeCacheManager.getBridgeNodes();
629 if (nodes.isEmpty()) {
630 LOG.trace("updateL3ForNeutronPort has no nodes to work with");
632 for (Node node : nodes) {
633 final Long dpid = getDpidForIntegrationBridge(node);
638 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
639 final String tenantIpStr = neutronIP.getIpAddress();
640 if (tenantIpStr.isEmpty()) {
644 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
645 // still needed when routing to subnets non-local to node (bug 2076).
646 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
648 // Configure distributed ARP responder
649 if (true == flgDistributedARPEnabled) {
650 programStaticArpStage1(dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
656 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
657 String macAddress, String ipStr,
658 Action actionForNode) {
659 // Based on the local cache, figure out whether programming needs to occur. To do this, we
660 // will look at desired action for node.
662 final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + ipStr;
663 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
665 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
666 LOG.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
667 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
670 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
671 LOG.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
672 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
676 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
677 macAddress, ipStr, actionForNode);
678 if (status.isSuccess()) {
680 if (actionForNode == Action.ADD) {
681 l3ForwardingCache.add(cacheKey);
683 l3ForwardingCache.remove(cacheKey);
688 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
691 Action actionForNode) {
694 InetAddress inetAddress = InetAddress.getByName(address);
695 status = l3ForwardingProvider == null ?
696 new Status(StatusCode.SUCCESS) :
697 l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
698 inetAddress, macAddress, actionForNode);
699 } catch (UnknownHostException e) {
700 status = new Status(StatusCode.BADREQUEST);
703 if (status.isSuccess()) {
704 LOG.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
705 l3ForwardingProvider == null ? "skipped" : "programmed",
706 macAddress, address, node.getNodeId().getValue(), actionForNode);
708 LOG.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
709 macAddress, address, node.getNodeId().getValue(), actionForNode, status);
716 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
718 Preconditions.checkNotNull(destNeutronRouterInterface);
720 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
721 String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
722 List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
723 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
724 final NeutronNetwork neutronNetwork = subnet != null ?
725 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
726 final String destinationSegmentationId = neutronNetwork != null ?
727 neutronNetwork.getProviderSegmentationID() : null;
728 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
729 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
730 final String cidr = subnet != null ? subnet.getCidr() : null;
731 final int mask = getMaskLenFromCidr(cidr);
733 LOG.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
734 destNeutronRouterInterface, isDelete);
736 // in delete path, mac address as well as ip address are not provided. Being so, let's find them from
738 if (neutronNetwork != null) {
739 if (macAddress == null || macAddress.isEmpty()) {
740 macAddress = networkIdToRouterMacCache.get(neutronNetwork.getNetworkUUID());
742 if (ipList == null || ipList.isEmpty()) {
743 ipList = networkIdToRouterIpListCache.get(neutronNetwork.getNetworkUUID());
747 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
748 cidr == null || cidr.isEmpty() ||
749 macAddress == null || macAddress.isEmpty() ||
750 ipList == null || ipList.isEmpty()) {
751 LOG.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
752 destinationSegmentationId, cidr, macAddress, ipList);
753 // done: go no further w/out all the info needed...
757 final Action actionForNode = isDelete ? Action.DELETE : Action.ADD;
759 // Keep cache for finding router's mac from network uuid -- add
762 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
763 networkIdToRouterIpListCache.put(neutronNetwork.getNetworkUUID(), new ArrayList<>(ipList));
764 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
767 List<Node> nodes = nodeCacheManager.getBridgeNodes();
768 if (nodes.isEmpty()) {
769 LOG.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
771 for (Node node : nodes) {
772 final Long dpid = getDpidForIntegrationBridge(node);
777 for (Neutron_IPs neutronIP : ipList) {
778 final String ipStr = neutronIP.getIpAddress();
779 if (ipStr.isEmpty()) {
780 LOG.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
781 node.getNodeId().getValue(), ipStr);
785 // Iterate through all other interfaces and add/remove reflexive flows to this interface
787 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
788 programFlowsForNeutronRouterInterfacePair(node, dpid,
789 srcNeutronRouterInterface, destNeutronRouterInterface,
790 neutronNetwork, destinationSegmentationId,
791 macAddress, ipStr, mask, actionForNode,
792 true /*isReflexsive*/);
796 programFlowForNetworkFromExternal(node, dpid, destinationSegmentationId, macAddress, ipStr, mask,
799 // Enable ARP responder by default, because router interface needs to be responded always.
800 programStaticArpStage1(dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
803 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
804 // for the external neutron networks.
807 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
808 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, cidr, actionForRewriteExclusion);
812 // Keep cache for finding router's mac from network uuid -- remove
815 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
816 networkIdToRouterIpListCache.remove(neutronNetwork.getNetworkUUID());
817 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
821 private void programFlowForNetworkFromExternal(final Node node,
823 final String destinationSegmentationId,
824 final String dstMacAddress,
825 final String destIpStr,
827 final Action actionForNode) {
828 programRouterInterfaceStage1(node, dpid, Constants.EXTERNAL_NETWORK, destinationSegmentationId,
829 dstMacAddress, destIpStr, destMask, actionForNode);
832 private void programFlowsForNeutronRouterInterfacePair(final Node node,
834 final NeutronRouter_Interface srcNeutronRouterInterface,
835 final NeutronRouter_Interface dstNeutronRouterInterface,
836 final NeutronNetwork dstNeutronNetwork,
837 final String destinationSegmentationId,
838 final String dstMacAddress,
839 final String destIpStr,
841 final Action actionForNode,
842 Boolean isReflexsive) {
843 Preconditions.checkNotNull(srcNeutronRouterInterface);
844 Preconditions.checkNotNull(dstNeutronRouterInterface);
846 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
847 if (sourceSubnetId == null) {
848 LOG.error("Could not get provider Subnet ID from router interface {}",
849 srcNeutronRouterInterface.getID());
853 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
854 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
855 if (sourceNetworkId == null) {
856 LOG.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
860 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
861 if (sourceNetwork == null) {
862 LOG.error("Could not get provider Network for Network ID {}", sourceNetworkId);
866 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
867 // Isolate subnets from different tenants within the same router
870 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
871 if (sourceSegmentationId == null) {
872 LOG.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
875 if (sourceSegmentationId.equals(destinationSegmentationId)) {
880 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
881 dstMacAddress, destIpStr, destMask, actionForNode);
883 // Flip roles src->dst; dst->src
885 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
886 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
887 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
888 final String cidr2 = sourceSubnet.getCidr();
889 final int mask2 = getMaskLenFromCidr(cidr2);
891 if (cidr2 == null || cidr2.isEmpty() ||
892 macAddress2 == null || macAddress2.isEmpty() ||
893 ipList2 == null || ipList2.isEmpty()) {
894 LOG.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
895 sourceSegmentationId, cidr2, macAddress2, ipList2);
896 // done: go no further w/out all the info needed...
900 for (Neutron_IPs neutronIP2 : ipList2) {
901 final String ipStr2 = neutronIP2.getIpAddress();
902 if (ipStr2.isEmpty()) {
905 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
906 srcNeutronRouterInterface,
907 sourceNetwork, sourceSegmentationId,
908 macAddress2, ipStr2, mask2, actionForNode,
909 false /*isReflexsive*/);
914 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
915 String destinationSegmentationId,
916 String macAddress, String ipStr, int mask,
917 Action actionForNode) {
918 // Based on the local cache, figure out whether programming needs to occur. To do this, we
919 // will look at desired action for node.
921 final String cacheKey = node.getNodeId().getValue() + ":" +
922 sourceSegmentationId + ":" + destinationSegmentationId + ":" +
923 ipStr + "/" + Integer.toString(mask);
924 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
926 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
927 LOG.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
928 " action {} is already done",
929 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
930 macAddress, ipStr, mask, actionForNode);
933 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
934 LOG.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
935 " action {} is already done",
936 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
937 macAddress, ipStr, mask, actionForNode);
941 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
942 macAddress, ipStr, mask, actionForNode);
943 if (status.isSuccess()) {
945 if (actionForNode == Action.ADD) {
946 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
947 routerInterfacesCache.add(cacheKey);
949 // TODO: multiTenantAwareRouter.removeInterface(...);
950 routerInterfacesCache.remove(cacheKey);
955 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
956 String destinationSegmentationId,
958 String address, int mask,
959 Action actionForNode) {
962 InetAddress inetAddress = InetAddress.getByName(address);
963 status = routingProvider == null ?
964 new Status(StatusCode.SUCCESS) :
965 routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
966 macAddress, inetAddress, mask, actionForNode);
967 } catch (UnknownHostException e) {
968 status = new Status(StatusCode.BADREQUEST);
971 if (status.isSuccess()) {
972 LOG.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
973 routingProvider == null ? "skipped" : "programmed",
974 macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
977 LOG.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
978 macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
979 actionForNode, status);
984 private boolean programStaticArpStage1(Long dpid, String segOrOfPort,
985 String macAddress, String ipStr,
987 // Based on the local cache, figure out whether programming needs to occur. To do this, we
988 // will look at desired action for node.
990 final String cacheKey = dpid + ":" + segOrOfPort + ":" + ipStr;
991 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
993 if (action == Action.DELETE && isProgrammed == Boolean.FALSE) {
994 LOG.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
995 dpid, segOrOfPort, macAddress, ipStr, action);
998 if (action == Action.ADD && isProgrammed == Boolean.TRUE) {
999 LOG.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
1000 dpid, segOrOfPort, macAddress, ipStr, action);
1004 Status status = this.programStaticArpStage2(dpid, segOrOfPort, macAddress, ipStr, action);
1005 if (status.isSuccess()) {
1007 if (action == Action.ADD) {
1008 staticArpEntryCache.add(cacheKey);
1010 staticArpEntryCache.remove(cacheKey);
1017 private Status programStaticArpStage2(Long dpid,
1024 InetAddress inetAddress = InetAddress.getByName(address);
1025 status = arpProvider == null ?
1026 new Status(StatusCode.SUCCESS) :
1027 arpProvider.programStaticArpEntry(dpid, segOrOfPort,
1028 macAddress, inetAddress, action);
1029 } catch (UnknownHostException e) {
1030 status = new Status(StatusCode.BADREQUEST);
1033 if (status.isSuccess()) {
1034 LOG.debug("ProgramStaticArp {} for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{}",
1035 arpProvider == null ? "skipped" : "programmed",
1036 macAddress, address, dpid, segOrOfPort, action);
1038 LOG.error("ProgramStaticArp failed for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{} status:{}",
1039 macAddress, address, dpid, segOrOfPort, action, status);
1044 private boolean programInboundIpRewriteStage1(Long dpid, Long inboundOFPort, String providerSegmentationId,
1045 String matchAddress, String rewriteAddress,
1047 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1048 // will look at desired action for node.
1050 final String cacheKey = dpid + ":" + inboundOFPort + ":" + providerSegmentationId + ":" + matchAddress;
1051 final Boolean isProgrammed = inboundIpRewriteCache.contains(cacheKey);
1053 if (action == Action.DELETE && isProgrammed == Boolean.FALSE) {
1054 LOG.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
1055 " action {} is already done",
1056 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1059 if (action == Action.ADD && isProgrammed == Boolean.TRUE) {
1060 LOG.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
1061 " action is already done",
1062 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1066 Status status = programInboundIpRewriteStage2(dpid, inboundOFPort, providerSegmentationId, matchAddress,
1067 rewriteAddress, action);
1068 if (status.isSuccess()) {
1070 if (action == Action.ADD) {
1071 inboundIpRewriteCache.add(cacheKey);
1073 inboundIpRewriteCache.remove(cacheKey);
1080 private Status programInboundIpRewriteStage2(Long dpid, Long inboundOFPort, String providerSegmentationId,
1081 String matchAddress, String rewriteAddress,
1085 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
1086 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
1087 status = inboundNatProvider == null ?
1088 new Status(StatusCode.SUCCESS) :
1089 inboundNatProvider.programIpRewriteRule(dpid, inboundOFPort, providerSegmentationId,
1090 inetMatchAddress, inetRewriteAddress,
1092 } catch (UnknownHostException e) {
1093 status = new Status(StatusCode.BADREQUEST);
1096 if (status.isSuccess()) {
1097 final boolean isSkipped = inboundNatProvider == null;
1098 LOG.debug("programInboundIpRewriteStage2 {} for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}",
1099 isSkipped ? "skipped" : "programmed",
1100 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1102 LOG.error("programInboundIpRewriteStage2 failed for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}" +
1104 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action,
1110 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId, String cidr,
1111 Action actionForRewriteExclusion) {
1112 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1113 // will look at desired action for node.
1115 final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + cidr;
1116 final Boolean isProgrammed = outboundIpRewriteExclusionCache.contains(cacheKey);
1118 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
1119 LOG.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done",
1120 node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
1123 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
1124 LOG.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done",
1125 node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
1129 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
1130 actionForRewriteExclusion);
1131 if (status.isSuccess()) {
1133 if (actionForRewriteExclusion == Action.ADD) {
1134 outboundIpRewriteExclusionCache.add(cacheKey);
1136 outboundIpRewriteExclusionCache.remove(cacheKey);
1141 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
1142 Action actionForNode) {
1143 final Status status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
1144 outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr, actionForNode);
1146 if (status.isSuccess()) {
1147 final boolean isSkipped = outboundNatProvider == null;
1148 LOG.debug("IpRewriteExclusion {} for cidr:{} node:{} action:{}",
1149 isSkipped ? "skipped" : "programmed",
1150 cidr, node.getNodeId().getValue(), actionForNode);
1152 LOG.error("IpRewriteExclusion failed for cidr:{} node:{} action:{} status:{}",
1153 cidr, node.getNodeId().getValue(), actionForNode, status);
1158 private void programOutboundIpRewriteStage1(FloatIpData fid, Action action) {
1159 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1160 // will look at desired action for node.
1162 final String cacheKey = fid.dpid + ":" + fid.segId + ":" + fid.fixedIpAddress;
1163 final Boolean isProgrammed = outboundIpRewriteCache.contains(cacheKey);
1165 if (action == Action.DELETE && isProgrammed == Boolean.FALSE) {
1166 LOG.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " +
1168 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1171 if (action == Action.ADD && isProgrammed == Boolean.TRUE) {
1172 LOG.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " +
1174 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1178 Status status = this.programOutboundIpRewriteStage2(fid, action);
1179 if (status.isSuccess()) {
1181 if (action == Action.ADD) {
1182 outboundIpRewriteCache.add(cacheKey);
1184 outboundIpRewriteCache.remove(cacheKey);
1189 private Status programOutboundIpRewriteStage2(FloatIpData fid, Action action) {
1192 InetAddress matchSrcAddress = InetAddress.getByName(fid.fixedIpAddress);
1193 InetAddress rewriteSrcAddress = InetAddress.getByName(fid.floatingIpAddress);
1194 status = outboundNatProvider == null ?
1195 new Status(StatusCode.SUCCESS) :
1196 outboundNatProvider.programIpRewriteRule(
1197 fid.dpid, fid.segId, fid.neutronRouterMac, matchSrcAddress, fid.macAddress,
1198 this.externalRouterMac, rewriteSrcAddress, fid.ofPort, action);
1199 } catch (UnknownHostException e) {
1200 status = new Status(StatusCode.BADREQUEST);
1203 if (status.isSuccess()) {
1204 final boolean isSkipped = outboundNatProvider == null;
1205 LOG.debug("programOutboundIpRewriteStage2 {} for dpid {} seg {} fixedIpAddress {} floatIp {}" +
1207 isSkipped ? "skipped" : "programmed",
1208 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1210 LOG.error("programOutboundIpRewriteStage2 failed for dpid {} seg {} fixedIpAddress {} floatIp {}" +
1211 " action {} status:{}",
1212 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action, status);
1217 private int getMaskLenFromCidr(String cidr) {
1221 String[] splits = cidr.split("/");
1222 if (splits.length != 2) {
1228 result = Integer.parseInt(splits[1].trim());
1229 } catch (NumberFormatException nfe) {
1235 private Long getDpidForIntegrationBridge(Node node) {
1236 // Check if node is integration bridge; and only then return its dpid
1237 if (southbound.getBridge(node, configurationService.getIntegrationBridgeName()) != null) {
1238 return southbound.getDataPathId(node);
1243 private Long getDpidForExternalBridge(Node node) {
1244 // Check if node is integration bridge; and only then return its dpid
1245 if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
1246 return southbound.getDataPathId(node);
1251 private Node getExternalBridgeNode(){
1252 //Pickup the first node that has external bridge (br-ex).
1253 //NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
1254 //the external network is reachable from every br-ex
1255 // TODO: Consider other deployment scenario, and thing of better solution.
1256 List<Node> allBridges = nodeCacheManager.getBridgeNodes();
1257 for(Node node : allBridges){
1258 if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
1265 private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
1266 NeutronSubnet extSubnet = null;
1267 for (NeutronSubnet subnet : neutronSubnetCache.getAllSubnets()){
1268 if(subnet.getPortsInSubnet().contains(gatewayPort)){
1276 public void triggerGatewayMacResolver(final Node node, final NeutronPort gatewayPort ){
1278 Preconditions.checkNotNull(node);
1279 Preconditions.checkNotNull(gatewayPort);
1280 NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
1282 if(externalNetwork != null){
1283 if(externalNetwork.isRouterExternal()){
1284 final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
1285 if(externalSubnet != null){
1286 if(externalSubnet.getGatewayIP() != null){
1287 LOG.info("Trigger MAC resolution for gateway ip {} on Node {}", externalSubnet.getGatewayIP(), node.getNodeId());
1289 ListenableFuture<MacAddress> gatewayMacAddress =
1290 gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
1291 new Ipv4Address(externalSubnet.getGatewayIP()),
1292 new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
1293 new MacAddress(gatewayPort.getMacAddress()),
1295 if(gatewayMacAddress != null){
1296 Futures.addCallback(gatewayMacAddress, new FutureCallback<MacAddress>(){
1298 public void onSuccess(MacAddress result) {
1300 updateExternalRouterMac(result.getValue());
1301 LOG.info("Resolved MAC address for gateway IP {} is {}", externalSubnet.getGatewayIP(), result.getValue());
1303 LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
1308 public void onFailure(Throwable t) {
1309 LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
1311 }, gatewayMacResolverPool);
1314 LOG.warn("No gateway IP address found for external subnet {}", externalSubnet);
1317 LOG.warn("Neutron subnet not found for external network {}", externalNetwork);
1321 LOG.warn("Neutron network not found for router interface {}", gatewayPort);
1326 * Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
1328 * @param ofPort the OF port number
1329 * @return the string with encoded OF port (example format "OFPort|999")
1331 public static String encodeExcplicitOFPort(Long ofPort) {
1332 return "OFPort|" + ofPort.toString();
1336 public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
1337 tenantNetworkManager =
1338 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
1339 configurationService =
1340 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
1342 (ArpProvider) ServiceHelper.getGlobalInstance(ArpProvider.class, this);
1343 inboundNatProvider =
1344 (InboundNatProvider) ServiceHelper.getGlobalInstance(InboundNatProvider.class, this);
1345 outboundNatProvider =
1346 (OutboundNatProvider) ServiceHelper.getGlobalInstance(OutboundNatProvider.class, this);
1348 (RoutingProvider) ServiceHelper.getGlobalInstance(RoutingProvider.class, this);
1349 l3ForwardingProvider =
1350 (L3ForwardingProvider) ServiceHelper.getGlobalInstance(L3ForwardingProvider.class, this);
1352 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
1354 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
1355 gatewayMacResolver =
1356 (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
1357 initL3AdapterMembers();
1361 public void setDependencies(Object impl) {
1362 if (impl instanceof INeutronNetworkCRUD) {
1363 neutronNetworkCache = (INeutronNetworkCRUD)impl;
1364 } else if (impl instanceof INeutronPortCRUD) {
1365 neutronPortCache = (INeutronPortCRUD)impl;
1366 } else if (impl instanceof INeutronSubnetCRUD) {
1367 neutronSubnetCache = (INeutronSubnetCRUD)impl;
1368 } else if (impl instanceof ArpProvider) {
1369 arpProvider = (ArpProvider)impl;
1370 } else if (impl instanceof InboundNatProvider) {
1371 inboundNatProvider = (InboundNatProvider)impl;
1372 } else if (impl instanceof OutboundNatProvider) {
1373 outboundNatProvider = (OutboundNatProvider)impl;
1374 } else if (impl instanceof RoutingProvider) {
1375 routingProvider = (RoutingProvider)impl;
1376 } else if (impl instanceof L3ForwardingProvider) {
1377 l3ForwardingProvider = (L3ForwardingProvider)impl;
1378 }else if (impl instanceof GatewayMacResolver) {
1379 gatewayMacResolver = (GatewayMacResolver)impl;