2 * Copyright (c) 2014, 2015 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.ovsdb.openstack.netvirt.impl;
11 import org.apache.commons.lang3.tuple.ImmutablePair;
12 import org.apache.commons.lang3.tuple.Pair;
13 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP;
14 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork;
15 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronPort;
16 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter;
17 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter_Interface;
18 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup;
19 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSubnet;
20 import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs;
21 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronNetworkCRUD;
22 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronPortCRUD;
23 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronSubnetCRUD;
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.ServiceReference;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 import java.net.InetAddress;
43 import java.net.UnknownHostException;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.ArrayList;
47 import java.util.Iterator;
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;
75 private volatile SecurityServicesManager securityServicesManager;
77 private class FloatIpData {
78 // br-int of node where floating ip is associated with tenant port
79 private final Long dpid;
80 // patch port in br-int used to reach br-ex
81 private final Long ofPort;
82 // segmentation id of the net where fixed ip is instantiated
83 private final String segId;
84 // mac address assigned to neutron port of floating ip
85 private final String macAddress;
86 private final String floatingIpAddress;
87 // ip address given to tenant vm
88 private final String fixedIpAddress;
89 private final String neutronRouterMac;
91 FloatIpData(final Long dpid, final Long ofPort, final String segId, final String macAddress,
92 final String floatingIpAddress, final String fixedIpAddress, final String neutronRouterMac) {
96 this.macAddress = macAddress;
97 this.floatingIpAddress = floatingIpAddress;
98 this.fixedIpAddress = fixedIpAddress;
99 this.neutronRouterMac = neutronRouterMac;
103 private Set<String> inboundIpRewriteCache;
104 private Set<String> outboundIpRewriteCache;
105 private Set<String> outboundIpRewriteExclusionCache;
106 private Set<String> routerInterfacesCache;
107 private Set<String> staticArpEntryCache;
108 private Set<String> l3ForwardingCache;
109 private Map<String, String> networkIdToRouterMacCache;
110 private Map<String, List<Neutron_IPs>> networkIdToRouterIpListCache;
111 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
112 private Map<String, Pair<Long, Uuid>> neutronPortToDpIdCache;
113 private Map<String, FloatIpData> floatIpDataMapCache;
114 private String externalRouterMac;
115 private Boolean enabled = false;
116 private Boolean flgDistributedARPEnabled = true;
117 private Southbound southbound;
118 private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5);
120 private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
121 private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
122 private static final String OWNER_ROUTER_GATEWAY = "network:router_gateway";
123 private static final String OWNER_FLOATING_IP = "network:floatingip";
124 private static final String DEFAULT_EXT_RTR_MAC = "00:00:5E:00:01:01";
126 public NeutronL3Adapter() {
127 LOG.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass());
130 private void initL3AdapterMembers() {
131 Preconditions.checkNotNull(configurationService);
133 if (configurationService.isL3ForwardingEnabled()) {
134 this.inboundIpRewriteCache = new HashSet<>();
135 this.outboundIpRewriteCache = new HashSet<>();
136 this.outboundIpRewriteExclusionCache = new HashSet<>();
137 this.routerInterfacesCache = new HashSet<>();
138 this.staticArpEntryCache = new HashSet<>();
139 this.l3ForwardingCache = new HashSet<>();
140 this.networkIdToRouterMacCache = new HashMap<>();
141 this.networkIdToRouterIpListCache = new HashMap<>();
142 this.subnetIdToRouterInterfaceCache = new HashMap<>();
143 this.neutronPortToDpIdCache = new HashMap<>();
144 this.floatIpDataMapCache = new HashMap<>();
146 this.externalRouterMac = configurationService.getDefaultGatewayMacAddress(null);
147 if (this.externalRouterMac == null) {
148 this.externalRouterMac = DEFAULT_EXT_RTR_MAC;
152 LOG.info("OVSDB L3 forwarding is enabled");
153 if (configurationService.isDistributedArpDisabled()) {
154 this.flgDistributedARPEnabled = false;
155 LOG.info("Distributed ARP responder is disabled");
157 LOG.debug("Distributed ARP responder is enabled");
160 LOG.debug("OVSDB L3 forwarding is disabled");
165 // Callbacks from OVSDB's northbound handlers
169 * Invoked to configure the mac address for the external gateway in br-ex. ovsdb netvirt needs help in getting
170 * mac for given ip in br-ex (bug 3378). For example, since ovsdb has no real arp, it needs a service in can
171 * subscribe so that the mac address associated to the gateway ip address is available.
173 * @param externalRouterMacUpdate The mac address to be associated to the gateway.
175 public void updateExternalRouterMac(final String externalRouterMacUpdate) {
176 Preconditions.checkNotNull(externalRouterMacUpdate);
178 flushExistingIpRewrite();
179 this.externalRouterMac = externalRouterMacUpdate;
180 rebuildExistingIpRewrite();
186 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
187 * @param subnet An instance of NeutronSubnet object.
189 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
190 LOG.debug("Neutron subnet {} event : {}", action, subnet.toString());
194 * Process the port event as a router interface event.
195 * For a not delete action, since a port is only create when the tennat uses the subnet, it is required to
196 * verify if all routers across all nodes have the interface for the port's subnet(s) configured.
198 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
199 * @param neutronPort An instance of NeutronPort object.
201 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
202 LOG.debug("Neutron port {} event : {}", action, neutronPort.toString());
204 this.processSecurityGroupUpdate(neutronPort);
209 final boolean isDelete = action == Action.DELETE;
211 if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
213 Node externalBridgeNode = getExternalBridgeNode();
214 if(externalBridgeNode != null){
215 LOG.info("Port {} is network router gateway interface, "
216 + "triggering gateway resolution for the attached external network on node {}", neutronPort, externalBridgeNode);
217 this.triggerGatewayMacResolver(externalBridgeNode, neutronPort);
219 LOG.error("Did not find Node that has external bridge (br-ex), Gateway resolution failed");
222 NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
224 if (externalNetwork != null && externalNetwork.isRouterExternal()) {
225 final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
227 if (externalSubnet != null &&
228 externalSubnet.getIpVersion() == 4) {
229 gatewayMacResolver.stopPeriodicRefresh(new Ipv4Address(externalSubnet.getGatewayIP()));
235 // Treat the port event as a router interface event if the port belongs to router. This is a
236 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
238 if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
239 neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
241 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
242 NeutronRouter_Interface neutronRouterInterface =
243 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
244 // id of router interface to be same as subnet
245 neutronRouterInterface.setID(neutronIP.getSubnetUUID());
246 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
248 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
251 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
252 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
253 // need to do this check here because a router interface is not added to a node until tenant becomes needed
256 if (!isDelete && neutronPort.getFixedIPs() != null) {
257 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
258 NeutronRouter_Interface neutronRouterInterface =
259 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
260 if (neutronRouterInterface != null) {
261 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
265 this.updateL3ForNeutronPort(neutronPort, isDelete);
272 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
273 * @param neutronRouter An instance of NeutronRouter object.
275 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
276 LOG.debug("Neutron router {} event : {}", action, neutronRouter.toString());
280 * Process the event enforcing actions and verifying dependencies between all router's interface. For example,
281 * delete the ports on the same subnet.
283 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
284 * @param neutronRouter An instance of NeutronRouter object.
285 * @param neutronRouterInterface An instance of NeutronRouter_Interface object.
287 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
288 final NeutronRouter_Interface neutronRouterInterface,
290 LOG.debug("Router interface {} got event {}. Subnet {}",
291 neutronRouterInterface.getPortUUID(),
293 neutronRouterInterface.getSubnetUUID());
298 final boolean isDelete = action == Action.DELETE;
300 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
302 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
303 // see if they are affected by l3
305 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
306 boolean currPortShouldBeDeleted = false;
307 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
309 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
310 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
311 currPortShouldBeDeleted = true;
316 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
321 * Bug 4277: Remove the router interface cache only after deleting the neutron port l3 flows.
323 this.cleanupRouterCache(neutronRouterInterface);
328 * Invoked when a neutron message regarding the floating ip association is sent to odl via ml2. If the action is
329 * a creation, it will first add ARP rules for the given floating ip and then configure the DNAT (rewrite the
330 * packets from the floating IP address to the internal fixed ip) rules on OpenFlow Table 30 and SNAT rules (other
331 * way around) on OpenFlow Table 100.
333 * @param actionIn the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
334 * @param neutronFloatingIP An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP} instance of NeutronFloatingIP object.
336 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
338 Preconditions.checkNotNull(neutronFloatingIP);
340 LOG.debug(" Floating IP {} {}<->{}, network uuid {}", actionIn,
341 neutronFloatingIP.getFixedIPAddress(),
342 neutronFloatingIP.getFloatingIPAddress(),
343 neutronFloatingIP.getFloatingNetworkUUID());
350 // Consider action to be delete if getFixedIPAddress is null
352 if (neutronFloatingIP.getFixedIPAddress() == null) {
353 action = Action.DELETE;
358 // this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
360 if (action != Action.DELETE) {
361 // must be first, as it updates floatIpDataMapCache
362 programFlowsForFloatingIPArpAdd(neutronFloatingIP);
364 programFlowsForFloatingIPInbound(neutronFloatingIP, Action.ADD);
365 programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.ADD);
367 programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.DELETE);
368 programFlowsForFloatingIPInbound(neutronFloatingIP, Action.DELETE);
370 // must be last, as it updates floatIpDataMapCache
371 programFlowsForFloatingIPArpDelete(neutronFloatingIP.getID());
376 * This method performs creation or deletion of in-bound rules into Table 30 for a existing available floating
377 * ip, otherwise for newer one.
379 private void programFlowsForFloatingIPInbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
380 Preconditions.checkNotNull(neutronFloatingIP);
382 final FloatIpData fid = floatIpDataMapCache.get(neutronFloatingIP.getID());
384 LOG.trace("programFlowsForFloatingIPInboundAdd {} for {} uuid {} not in local cache",
385 action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
388 programInboundIpRewriteStage1(fid.dpid, fid.ofPort, fid.segId, fid.floatingIpAddress, fid.fixedIpAddress,
393 * This method performs creation or deletion of out-bound rules into Table 100 for a existing available floating
394 * ip, otherwise for newer one.
396 private void programFlowsForFloatingIPOutbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
397 Preconditions.checkNotNull(neutronFloatingIP);
399 final FloatIpData fid = floatIpDataMapCache.get(neutronFloatingIP.getID());
401 LOG.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache",
402 action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
405 programOutboundIpRewriteStage1(fid, action);
408 private void flushExistingIpRewrite() {
409 for (FloatIpData fid : floatIpDataMapCache.values()) {
410 programOutboundIpRewriteStage1(fid, Action.DELETE);
414 private void rebuildExistingIpRewrite() {
415 for (FloatIpData fid : floatIpDataMapCache.values()) {
416 programOutboundIpRewriteStage1(fid, Action.ADD);
421 * This method creates ARP response rules into OpenFlow Table 30 for a given floating ip. In order to connect
422 * to br-ex from br-int, a patch-port is used. Thus, the patch-port will be responsible to respond the ARP
425 private void programFlowsForFloatingIPArpAdd(final NeutronFloatingIP neutronFloatingIP) {
426 Preconditions.checkNotNull(neutronFloatingIP);
427 Preconditions.checkNotNull(neutronFloatingIP.getFixedIPAddress());
428 Preconditions.checkNotNull(neutronFloatingIP.getFloatingIPAddress());
430 if (floatIpDataMapCache.get(neutronFloatingIP.getID()) != null) {
431 LOG.trace("programFlowsForFloatingIPArpAdd for neutronFloatingIP {} uuid {} is already done",
432 neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
436 // find bridge Node where floating ip is configured by looking up cache for its port
437 final NeutronPort neutronPortForFloatIp = findNeutronPortForFloatingIp(neutronFloatingIP.getID());
438 final String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
439 final Pair<Long, Uuid> nodeIfPair = neutronPortToDpIdCache.get(neutronTenantPortUuid);
440 final String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
441 final String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
442 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
444 final NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
445 final NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
446 neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
447 final String providerSegmentationId = tenantNeutronNetwork != null ?
448 tenantNeutronNetwork.getProviderSegmentationID() : null;
449 final String neutronRouterMac = tenantNeutronNetwork != null ?
450 networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
452 if (nodeIfPair == null || neutronTenantPortUuid == null ||
453 providerSegmentationId == null || providerSegmentationId.isEmpty() ||
454 floatingIpMac == null || floatingIpMac.isEmpty() ||
455 neutronRouterMac == null || neutronRouterMac.isEmpty()) {
456 LOG.trace("Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} seg {} mac {} rtrMac {}",
459 neutronPortForFloatIp,
460 neutronTenantPortUuid,
461 providerSegmentationId,
467 // get ofport for patch port in br-int
468 final Long dpId = nodeIfPair.getLeft();
469 final Long ofPort = findOFPortForExtPatch(dpId);
470 if (ofPort == null) {
471 LOG.warn("Unable to locate OF port of patch port to connect floating ip to external bridge. dpid {}",
476 // Respond to ARPs for the floating ip address by default, via the patch port that connects br-int to br-ex
478 if (programStaticArpStage1(dpId, encodeExcplicitOFPort(ofPort), floatingIpMac, floatingIpAddress,
480 final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
481 floatingIpAddress, fixedIpAddress, neutronRouterMac);
482 floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
483 LOG.info("Floating IP {}<->{} programmed ARP mac {} on OFport {} seg {} dpid {}",
484 neutronFloatingIP.getFixedIPAddress(), neutronFloatingIP.getFloatingIPAddress(),
485 floatingIpMac, ofPort, providerSegmentationId, dpId);
489 private void programFlowsForFloatingIPArpDelete(final String neutronFloatingIPUuid) {
490 final FloatIpData floatIpData = floatIpDataMapCache.get(neutronFloatingIPUuid);
491 if (floatIpData == null) {
492 LOG.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid);
496 if (programStaticArpStage1(floatIpData.dpid, encodeExcplicitOFPort(floatIpData.ofPort), floatIpData.macAddress,
497 floatIpData.floatingIpAddress, Action.DELETE)) {
498 floatIpDataMapCache.remove(neutronFloatingIPUuid);
499 LOG.info("Floating IP {} un-programmed ARP mac {} on {} dpid {}",
500 floatIpData.floatingIpAddress, floatIpData.macAddress, floatIpData.ofPort, floatIpData.dpid);
504 private NeutronPort findNeutronPortForFloatingIp(final String floatingIpUuid) {
505 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
506 if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
507 neutronPort.getDeviceID().equals(floatingIpUuid)) {
514 private Long findOFPortForExtPatch(Long dpId) {
515 final String brInt = configurationService.getIntegrationBridgeName();
516 final String brExt = configurationService.getExternalBridgeName();
517 final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
519 Preconditions.checkNotNull(dpId);
520 Preconditions.checkNotNull(portNameInt);
522 final long dpidPrimitive = dpId;
523 for (Node node : nodeCacheManager.getBridgeNodes()) {
524 if (dpidPrimitive == southbound.getDataPathId(node)) {
525 final OvsdbTerminationPointAugmentation terminationPointOfBridge =
526 southbound.getTerminationPointOfBridge(node, portNameInt);
527 return terminationPointOfBridge == null ? null : terminationPointOfBridge.getOfport();
536 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
537 * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronFloatingIP object.
539 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
540 LOG.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
544 // Callbacks from OVSDB's southbound handler
549 * @param bridgeNode An instance of Node object.
550 * @param intf An {@link org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105
551 * .OvsdbTerminationPointAugmentation} instance of OvsdbTerminationPointAugmentation object.
552 * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronNetwork
554 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
556 public void handleInterfaceEvent(final Node bridgeNode, final OvsdbTerminationPointAugmentation intf,
557 final NeutronNetwork neutronNetwork, Action action) {
558 LOG.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
559 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork);
564 final NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
565 final Long dpId = getDpidForIntegrationBridge(bridgeNode);
566 final Uuid interfaceUuid = intf.getInterfaceUuid();
568 LOG.trace("southbound interface {} node:{} interface:{}, neutronNetwork:{} port:{} dpid:{} intfUuid:{}",
569 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork, neutronPort, dpId, interfaceUuid);
571 if (neutronPort != null) {
572 final String neutronPortUuid = neutronPort.getPortUUID();
574 if (action != Action.DELETE && neutronPortToDpIdCache.get(neutronPortUuid) == null &&
575 dpId != null && interfaceUuid != null) {
576 handleInterfaceEventAdd(neutronPortUuid, dpId, interfaceUuid);
579 handleNeutronPortEvent(neutronPort, action);
582 if (action == Action.DELETE && interfaceUuid != null) {
583 handleInterfaceEventDelete(intf, dpId);
587 private void handleInterfaceEventAdd(final String neutronPortUuid, Long dpId, final Uuid interfaceUuid) {
588 neutronPortToDpIdCache.put(neutronPortUuid, new ImmutablePair<>(dpId, interfaceUuid));
589 LOG.debug("handleInterfaceEvent add cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
590 neutronPortUuid, dpId, interfaceUuid.getValue());
593 private void handleInterfaceEventDelete(final OvsdbTerminationPointAugmentation intf, final Long dpId) {
594 // Remove entry from neutronPortToDpIdCache based on interface uuid
595 for (Map.Entry<String, Pair<Long, Uuid>> entry : neutronPortToDpIdCache.entrySet()) {
596 final String currPortUuid = entry.getKey();
597 if (intf.getInterfaceUuid().equals(entry.getValue().getRight())) {
598 LOG.debug("handleInterfaceEventDelete remove cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
599 currPortUuid, dpId, intf.getInterfaceUuid().getValue());
600 neutronPortToDpIdCache.remove(currPortUuid);
609 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
611 final String networkUUID = neutronPort.getNetworkUUID();
612 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
614 // If there is no router interface handling the networkUUID, we are done
615 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
619 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
620 // router interface are handled via handleNeutronRouterInterfaceEvent.
621 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
625 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
626 final String providerSegmentationId = neutronNetwork != null ?
627 neutronNetwork.getProviderSegmentationID() : null;
628 final String tenantMac = neutronPort.getMacAddress();
630 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
631 tenantMac == null || tenantMac.isEmpty()) {
632 // done: go no further w/out all the info needed...
636 final Action action = isDelete ? Action.DELETE : Action.ADD;
637 List<Node> nodes = nodeCacheManager.getBridgeNodes();
638 if (nodes.isEmpty()) {
639 LOG.trace("updateL3ForNeutronPort has no nodes to work with");
641 for (Node node : nodes) {
642 final Long dpid = getDpidForIntegrationBridge(node);
647 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
648 final String tenantIpStr = neutronIP.getIpAddress();
649 if (tenantIpStr.isEmpty()) {
653 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
654 // still needed when routing to subnets non-local to node (bug 2076).
655 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
657 // Configure distributed ARP responder
658 if (flgDistributedARPEnabled) {
659 programStaticArpStage1(dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
665 private void processSecurityGroupUpdate(NeutronPort neutronPort) {
666 LOG.trace("processSecurityGroupUpdate:" + neutronPort);
668 * Get updated data and original data for the the changed. Identify the security groups that got
669 * added and removed and call the appropriate providers for updating the flows.
672 NeutronPort originalPort = neutronPort.getOriginalPort();
673 if (null == originalPort) {
674 LOG.debug("processSecurityGroupUpdate: originalport is empty");
677 List<NeutronSecurityGroup> addedGroup = getsecurityGroupChanged(neutronPort,
678 neutronPort.getOriginalPort());
679 List<NeutronSecurityGroup> deletedGroup = getsecurityGroupChanged(neutronPort.getOriginalPort(),
682 if (null != addedGroup && !addedGroup.isEmpty()) {
683 securityServicesManager.syncSecurityGroup(neutronPort,addedGroup,true);
685 if (null != deletedGroup && !deletedGroup.isEmpty()) {
686 securityServicesManager.syncSecurityGroup(neutronPort,deletedGroup,false);
689 } catch (Exception e) {
690 LOG.error("Exception in processSecurityGroupUpdate", e);
694 private List<NeutronSecurityGroup> getsecurityGroupChanged(NeutronPort port1, NeutronPort port2) {
695 LOG.trace("getsecurityGroupChanged:" + "Port1:" + port1 + "Port2" + port2);
696 ArrayList<NeutronSecurityGroup> list1 = new ArrayList<NeutronSecurityGroup>(port1.getSecurityGroups());
697 ArrayList<NeutronSecurityGroup> list2 = new ArrayList<NeutronSecurityGroup>(port2.getSecurityGroups());
698 for (Iterator<NeutronSecurityGroup> iterator = list1.iterator(); iterator.hasNext();) {
699 NeutronSecurityGroup securityGroup1 = iterator.next();
700 for (NeutronSecurityGroup securityGroup2 :list2) {
701 if (securityGroup1.getID().equals(securityGroup2.getID())) {
709 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
710 String macAddress, String ipStr,
711 Action actionForNode) {
712 // Based on the local cache, figure out whether programming needs to occur. To do this, we
713 // will look at desired action for node.
715 final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + ipStr;
716 final boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
718 if (actionForNode == Action.DELETE && !isProgrammed) {
719 LOG.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
720 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
723 if (actionForNode == Action.ADD && isProgrammed) {
724 LOG.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
725 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
729 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
730 macAddress, ipStr, actionForNode);
731 if (status.isSuccess()) {
733 if (actionForNode == Action.ADD) {
734 l3ForwardingCache.add(cacheKey);
736 l3ForwardingCache.remove(cacheKey);
741 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
744 Action actionForNode) {
747 InetAddress inetAddress = InetAddress.getByName(address);
748 status = l3ForwardingProvider == null ?
749 new Status(StatusCode.SUCCESS) :
750 l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
751 inetAddress, macAddress, actionForNode);
752 } catch (UnknownHostException e) {
753 status = new Status(StatusCode.BADREQUEST);
756 if (status.isSuccess()) {
757 LOG.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
758 l3ForwardingProvider == null ? "skipped" : "programmed",
759 macAddress, address, node.getNodeId().getValue(), actionForNode);
761 LOG.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
762 macAddress, address, node.getNodeId().getValue(), actionForNode, status);
769 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
771 Preconditions.checkNotNull(destNeutronRouterInterface);
773 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
774 String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
775 List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
776 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
777 final NeutronNetwork neutronNetwork = subnet != null ?
778 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
779 final String destinationSegmentationId = neutronNetwork != null ?
780 neutronNetwork.getProviderSegmentationID() : null;
781 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
782 final String cidr = subnet != null ? subnet.getCidr() : null;
783 final int mask = getMaskLenFromCidr(cidr);
785 LOG.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
786 destNeutronRouterInterface, isDelete);
788 // in delete path, mac address as well as ip address are not provided. Being so, let's find them from
790 if (neutronNetwork != null) {
791 if (macAddress == null || macAddress.isEmpty()) {
792 macAddress = networkIdToRouterMacCache.get(neutronNetwork.getNetworkUUID());
794 if (ipList == null || ipList.isEmpty()) {
795 ipList = networkIdToRouterIpListCache.get(neutronNetwork.getNetworkUUID());
799 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
800 cidr == null || cidr.isEmpty() ||
801 macAddress == null || macAddress.isEmpty() ||
802 ipList == null || ipList.isEmpty()) {
803 LOG.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
804 destinationSegmentationId, cidr, macAddress, ipList);
805 // done: go no further w/out all the info needed...
809 final Action actionForNode = isDelete ? Action.DELETE : Action.ADD;
811 // Keep cache for finding router's mac from network uuid -- add
814 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
815 networkIdToRouterIpListCache.put(neutronNetwork.getNetworkUUID(), new ArrayList<>(ipList));
816 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
819 List<Node> nodes = nodeCacheManager.getBridgeNodes();
820 if (nodes.isEmpty()) {
821 LOG.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
823 for (Node node : nodes) {
824 final Long dpid = getDpidForIntegrationBridge(node);
829 for (Neutron_IPs neutronIP : ipList) {
830 final String ipStr = neutronIP.getIpAddress();
831 if (ipStr.isEmpty()) {
832 LOG.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
833 node.getNodeId().getValue(), ipStr);
837 // Iterate through all other interfaces and add/remove reflexive flows to this interface
839 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
840 programFlowsForNeutronRouterInterfacePair(node, dpid,
841 srcNeutronRouterInterface, destNeutronRouterInterface,
842 neutronNetwork, destinationSegmentationId,
843 macAddress, ipStr, mask, actionForNode,
844 true /*isReflexsive*/);
848 programFlowForNetworkFromExternal(node, dpid, destinationSegmentationId, macAddress, ipStr, mask,
851 // Enable ARP responder by default, because router interface needs to be responded always.
852 programStaticArpStage1(dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
855 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
856 // for the external neutron networks.
859 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
860 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, cidr, actionForRewriteExclusion);
864 // Keep cache for finding router's mac from network uuid -- NOTE: remove is done later, via cleanupRouterCache()
867 private void programFlowForNetworkFromExternal(final Node node,
869 final String destinationSegmentationId,
870 final String dstMacAddress,
871 final String destIpStr,
873 final Action actionForNode) {
874 programRouterInterfaceStage1(node, dpid, Constants.EXTERNAL_NETWORK, destinationSegmentationId,
875 dstMacAddress, destIpStr, destMask, actionForNode);
878 private void programFlowsForNeutronRouterInterfacePair(final Node node,
880 final NeutronRouter_Interface srcNeutronRouterInterface,
881 final NeutronRouter_Interface dstNeutronRouterInterface,
882 final NeutronNetwork dstNeutronNetwork,
883 final String destinationSegmentationId,
884 final String dstMacAddress,
885 final String destIpStr,
887 final Action actionForNode,
888 Boolean isReflexsive) {
889 Preconditions.checkNotNull(srcNeutronRouterInterface);
890 Preconditions.checkNotNull(dstNeutronRouterInterface);
892 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
893 if (sourceSubnetId == null) {
894 LOG.error("Could not get provider Subnet ID from router interface {}",
895 srcNeutronRouterInterface.getID());
899 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
900 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
901 if (sourceNetworkId == null) {
902 LOG.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
906 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
907 if (sourceNetwork == null) {
908 LOG.error("Could not get provider Network for Network ID {}", sourceNetworkId);
912 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
913 // Isolate subnets from different tenants within the same router
916 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
917 if (sourceSegmentationId == null) {
918 LOG.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
921 if (sourceSegmentationId.equals(destinationSegmentationId)) {
926 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
927 dstMacAddress, destIpStr, destMask, actionForNode);
929 // Flip roles src->dst; dst->src
931 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
932 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
933 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
934 final String cidr2 = sourceSubnet.getCidr();
935 final int mask2 = getMaskLenFromCidr(cidr2);
937 if (cidr2 == null || cidr2.isEmpty() ||
938 macAddress2 == null || macAddress2.isEmpty() ||
939 ipList2 == null || ipList2.isEmpty()) {
940 LOG.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
941 sourceSegmentationId, cidr2, macAddress2, ipList2);
942 // done: go no further w/out all the info needed...
946 for (Neutron_IPs neutronIP2 : ipList2) {
947 final String ipStr2 = neutronIP2.getIpAddress();
948 if (ipStr2.isEmpty()) {
951 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
952 srcNeutronRouterInterface,
953 sourceNetwork, sourceSegmentationId,
954 macAddress2, ipStr2, mask2, actionForNode,
955 false /*isReflexsive*/);
960 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
961 String destinationSegmentationId,
962 String macAddress, String ipStr, int mask,
963 Action actionForNode) {
964 // Based on the local cache, figure out whether programming needs to occur. To do this, we
965 // will look at desired action for node.
967 final String cacheKey = node.getNodeId().getValue() + ":" +
968 sourceSegmentationId + ":" + destinationSegmentationId + ":" +
969 ipStr + "/" + Integer.toString(mask);
970 final boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
972 if (actionForNode == Action.DELETE && !isProgrammed) {
973 LOG.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
974 " action {} is already done",
975 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
976 macAddress, ipStr, mask, actionForNode);
979 if (actionForNode == Action.ADD && isProgrammed) {
980 LOG.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
981 " action {} is already done",
982 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
983 macAddress, ipStr, mask, actionForNode);
987 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
988 macAddress, ipStr, mask, actionForNode);
989 if (status.isSuccess()) {
991 if (actionForNode == Action.ADD) {
992 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
993 routerInterfacesCache.add(cacheKey);
995 // TODO: multiTenantAwareRouter.removeInterface(...);
996 routerInterfacesCache.remove(cacheKey);
1001 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
1002 String destinationSegmentationId,
1004 String address, int mask,
1005 Action actionForNode) {
1008 InetAddress inetAddress = InetAddress.getByName(address);
1009 status = routingProvider == null ?
1010 new Status(StatusCode.SUCCESS) :
1011 routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
1012 macAddress, inetAddress, mask, actionForNode);
1013 } catch (UnknownHostException e) {
1014 status = new Status(StatusCode.BADREQUEST);
1017 if (status.isSuccess()) {
1018 LOG.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
1019 routingProvider == null ? "skipped" : "programmed",
1020 macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
1023 LOG.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
1024 macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
1025 actionForNode, status);
1030 private boolean programStaticArpStage1(Long dpid, String segOrOfPort,
1031 String macAddress, String ipStr,
1033 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1034 // will look at desired action for node.
1036 final String cacheKey = dpid + ":" + segOrOfPort + ":" + ipStr;
1037 final boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
1039 if (action == Action.DELETE && !isProgrammed) {
1040 LOG.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
1041 dpid, segOrOfPort, macAddress, ipStr, action);
1044 if (action == Action.ADD && isProgrammed) {
1045 LOG.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
1046 dpid, segOrOfPort, macAddress, ipStr, action);
1050 Status status = this.programStaticArpStage2(dpid, segOrOfPort, macAddress, ipStr, action);
1051 if (status.isSuccess()) {
1053 if (action == Action.ADD) {
1054 staticArpEntryCache.add(cacheKey);
1056 staticArpEntryCache.remove(cacheKey);
1063 private Status programStaticArpStage2(Long dpid,
1070 InetAddress inetAddress = InetAddress.getByName(address);
1071 status = arpProvider == null ?
1072 new Status(StatusCode.SUCCESS) :
1073 arpProvider.programStaticArpEntry(dpid, segOrOfPort,
1074 macAddress, inetAddress, action);
1075 } catch (UnknownHostException e) {
1076 status = new Status(StatusCode.BADREQUEST);
1079 if (status.isSuccess()) {
1080 LOG.debug("ProgramStaticArp {} for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{}",
1081 arpProvider == null ? "skipped" : "programmed",
1082 macAddress, address, dpid, segOrOfPort, action);
1084 LOG.error("ProgramStaticArp failed for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{} status:{}",
1085 macAddress, address, dpid, segOrOfPort, action, status);
1090 private boolean programInboundIpRewriteStage1(Long dpid, Long inboundOFPort, String providerSegmentationId,
1091 String matchAddress, String rewriteAddress,
1093 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1094 // will look at desired action for node.
1096 final String cacheKey = dpid + ":" + inboundOFPort + ":" + providerSegmentationId + ":" + matchAddress;
1097 final boolean isProgrammed = inboundIpRewriteCache.contains(cacheKey);
1099 if (action == Action.DELETE && !isProgrammed) {
1100 LOG.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
1101 " action {} is already done",
1102 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1105 if (action == Action.ADD && isProgrammed) {
1106 LOG.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
1107 " action is already done",
1108 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1112 Status status = programInboundIpRewriteStage2(dpid, inboundOFPort, providerSegmentationId, matchAddress,
1113 rewriteAddress, action);
1114 if (status.isSuccess()) {
1116 if (action == Action.ADD) {
1117 inboundIpRewriteCache.add(cacheKey);
1119 inboundIpRewriteCache.remove(cacheKey);
1126 private Status programInboundIpRewriteStage2(Long dpid, Long inboundOFPort, String providerSegmentationId,
1127 String matchAddress, String rewriteAddress,
1131 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
1132 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
1133 status = inboundNatProvider == null ?
1134 new Status(StatusCode.SUCCESS) :
1135 inboundNatProvider.programIpRewriteRule(dpid, inboundOFPort, providerSegmentationId,
1136 inetMatchAddress, inetRewriteAddress,
1138 } catch (UnknownHostException e) {
1139 status = new Status(StatusCode.BADREQUEST);
1142 if (status.isSuccess()) {
1143 final boolean isSkipped = inboundNatProvider == null;
1144 LOG.debug("programInboundIpRewriteStage2 {} for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}",
1145 isSkipped ? "skipped" : "programmed",
1146 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1148 LOG.error("programInboundIpRewriteStage2 failed for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}" +
1150 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action,
1156 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId, String cidr,
1157 Action actionForRewriteExclusion) {
1158 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1159 // will look at desired action for node.
1161 final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + cidr;
1162 final boolean isProgrammed = outboundIpRewriteExclusionCache.contains(cacheKey);
1164 if (actionForRewriteExclusion == Action.DELETE && !isProgrammed) {
1165 LOG.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done",
1166 node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
1169 if (actionForRewriteExclusion == Action.ADD && isProgrammed) {
1170 LOG.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done",
1171 node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
1175 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
1176 actionForRewriteExclusion);
1177 if (status.isSuccess()) {
1179 if (actionForRewriteExclusion == Action.ADD) {
1180 outboundIpRewriteExclusionCache.add(cacheKey);
1182 outboundIpRewriteExclusionCache.remove(cacheKey);
1187 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
1188 Action actionForNode) {
1189 final Status status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
1190 outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr, actionForNode);
1192 if (status.isSuccess()) {
1193 final boolean isSkipped = outboundNatProvider == null;
1194 LOG.debug("IpRewriteExclusion {} for cidr:{} node:{} action:{}",
1195 isSkipped ? "skipped" : "programmed",
1196 cidr, node.getNodeId().getValue(), actionForNode);
1198 LOG.error("IpRewriteExclusion failed for cidr:{} node:{} action:{} status:{}",
1199 cidr, node.getNodeId().getValue(), actionForNode, status);
1204 private void programOutboundIpRewriteStage1(FloatIpData fid, Action action) {
1205 // Based on the local cache, figure out whether programming needs to occur. To do this, we
1206 // will look at desired action for node.
1208 final String cacheKey = fid.dpid + ":" + fid.segId + ":" + fid.fixedIpAddress;
1209 final boolean isProgrammed = outboundIpRewriteCache.contains(cacheKey);
1211 if (action == Action.DELETE && !isProgrammed) {
1212 LOG.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " +
1214 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1217 if (action == Action.ADD && isProgrammed) {
1218 LOG.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " +
1220 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1224 Status status = this.programOutboundIpRewriteStage2(fid, action);
1225 if (status.isSuccess()) {
1227 if (action == Action.ADD) {
1228 outboundIpRewriteCache.add(cacheKey);
1230 outboundIpRewriteCache.remove(cacheKey);
1235 private Status programOutboundIpRewriteStage2(FloatIpData fid, Action action) {
1238 InetAddress matchSrcAddress = InetAddress.getByName(fid.fixedIpAddress);
1239 InetAddress rewriteSrcAddress = InetAddress.getByName(fid.floatingIpAddress);
1240 status = outboundNatProvider == null ?
1241 new Status(StatusCode.SUCCESS) :
1242 outboundNatProvider.programIpRewriteRule(
1243 fid.dpid, fid.segId, fid.neutronRouterMac, matchSrcAddress, fid.macAddress,
1244 this.externalRouterMac, rewriteSrcAddress, fid.ofPort, action);
1245 } catch (UnknownHostException e) {
1246 status = new Status(StatusCode.BADREQUEST);
1249 if (status.isSuccess()) {
1250 final boolean isSkipped = outboundNatProvider == null;
1251 LOG.debug("programOutboundIpRewriteStage2 {} for dpid {} seg {} fixedIpAddress {} floatIp {}" +
1253 isSkipped ? "skipped" : "programmed",
1254 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1256 LOG.error("programOutboundIpRewriteStage2 failed for dpid {} seg {} fixedIpAddress {} floatIp {}" +
1257 " action {} status:{}",
1258 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action, status);
1263 private int getMaskLenFromCidr(String cidr) {
1267 String[] splits = cidr.split("/");
1268 if (splits.length != 2) {
1274 result = Integer.parseInt(splits[1].trim());
1275 } catch (NumberFormatException nfe) {
1281 private Long getDpidForIntegrationBridge(Node node) {
1282 // Check if node is integration bridge; and only then return its dpid
1283 if (southbound.getBridge(node, configurationService.getIntegrationBridgeName()) != null) {
1284 return southbound.getDataPathId(node);
1289 private Long getDpidForExternalBridge(Node node) {
1290 // Check if node is integration bridge; and only then return its dpid
1291 if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
1292 return southbound.getDataPathId(node);
1297 private Node getExternalBridgeNode(){
1298 //Pickup the first node that has external bridge (br-ex).
1299 //NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
1300 //the external network is reachable from every br-ex
1301 // TODO: Consider other deployment scenario, and thing of better solution.
1302 List<Node> allBridges = nodeCacheManager.getBridgeNodes();
1303 for(Node node : allBridges){
1304 if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
1311 private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
1312 for (Neutron_IPs neutronIPs : gatewayPort.getFixedIPs()) {
1313 String subnetUUID = neutronIPs.getSubnetUUID();
1314 NeutronSubnet extSubnet = neutronSubnetCache.getSubnet(subnetUUID);
1315 if (extSubnet != null && extSubnet.getGatewayIP() != null) {
1318 if (extSubnet == null) {
1319 // TODO: when subnet is created, try again.
1320 LOG.debug("subnet {} in not found", subnetUUID);
1326 private void cleanupRouterCache(final NeutronRouter_Interface neutronRouterInterface) {
1329 * Remove the router cache only after deleting the neutron
1332 final NeutronPort neutronPort = neutronPortCache.getPort(neutronRouterInterface.getPortUUID());
1334 if (neutronPort != null) {
1335 networkIdToRouterMacCache.remove(neutronPort.getNetworkUUID());
1336 networkIdToRouterIpListCache.remove(neutronPort.getNetworkUUID());
1337 subnetIdToRouterInterfaceCache.remove(neutronRouterInterface.getSubnetUUID());
1341 public void triggerGatewayMacResolver(final Node node, final NeutronPort gatewayPort ){
1343 Preconditions.checkNotNull(node);
1344 Preconditions.checkNotNull(gatewayPort);
1345 NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
1347 if(externalNetwork != null){
1348 if(externalNetwork.isRouterExternal()){
1349 final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
1351 // TODO: address IPv6 case.
1352 if (externalSubnet != null &&
1353 externalSubnet.getIpVersion() == 4) {
1354 LOG.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId());
1355 ListenableFuture<MacAddress> gatewayMacAddress =
1356 gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
1357 new Ipv4Address(externalSubnet.getGatewayIP()),
1358 new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
1359 new MacAddress(gatewayPort.getMacAddress()),
1361 if(gatewayMacAddress != null){
1362 Futures.addCallback(gatewayMacAddress, new FutureCallback<MacAddress>(){
1364 public void onSuccess(MacAddress result) {
1366 if(!result.getValue().equals(externalRouterMac)){
1367 updateExternalRouterMac(result.getValue());
1368 LOG.info("Resolved MAC address for gateway IP {} is {}", externalSubnet.getGatewayIP(),result.getValue());
1371 LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
1376 public void onFailure(Throwable t) {
1377 LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
1379 }, gatewayMacResolverPool);
1382 LOG.warn("No gateway IP address found for external network {}", externalNetwork);
1386 LOG.warn("Neutron network not found for router interface {}", gatewayPort);
1391 * Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
1393 * @param ofPort the OF port number
1394 * @return the string with encoded OF port (example format "OFPort|999")
1396 public static String encodeExcplicitOFPort(Long ofPort) {
1397 return "OFPort|" + ofPort.toString();
1401 public void setDependencies(ServiceReference serviceReference) {
1402 tenantNetworkManager =
1403 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
1404 configurationService =
1405 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
1407 (ArpProvider) ServiceHelper.getGlobalInstance(ArpProvider.class, this);
1408 inboundNatProvider =
1409 (InboundNatProvider) ServiceHelper.getGlobalInstance(InboundNatProvider.class, this);
1410 outboundNatProvider =
1411 (OutboundNatProvider) ServiceHelper.getGlobalInstance(OutboundNatProvider.class, this);
1413 (RoutingProvider) ServiceHelper.getGlobalInstance(RoutingProvider.class, this);
1414 l3ForwardingProvider =
1415 (L3ForwardingProvider) ServiceHelper.getGlobalInstance(L3ForwardingProvider.class, this);
1417 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
1419 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
1420 gatewayMacResolver =
1421 (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
1422 securityServicesManager =
1423 (SecurityServicesManager) ServiceHelper.getGlobalInstance(SecurityServicesManager.class, this);
1424 initL3AdapterMembers();
1428 public void setDependencies(Object impl) {
1429 if (impl instanceof INeutronNetworkCRUD) {
1430 neutronNetworkCache = (INeutronNetworkCRUD)impl;
1431 } else if (impl instanceof INeutronPortCRUD) {
1432 neutronPortCache = (INeutronPortCRUD)impl;
1433 } else if (impl instanceof INeutronSubnetCRUD) {
1434 neutronSubnetCache = (INeutronSubnetCRUD)impl;
1435 } else if (impl instanceof ArpProvider) {
1436 arpProvider = (ArpProvider)impl;
1437 } else if (impl instanceof InboundNatProvider) {
1438 inboundNatProvider = (InboundNatProvider)impl;
1439 } else if (impl instanceof OutboundNatProvider) {
1440 outboundNatProvider = (OutboundNatProvider)impl;
1441 } else if (impl instanceof RoutingProvider) {
1442 routingProvider = (RoutingProvider)impl;
1443 } else if (impl instanceof L3ForwardingProvider) {
1444 l3ForwardingProvider = (L3ForwardingProvider)impl;
1445 }else if (impl instanceof GatewayMacResolver) {
1446 gatewayMacResolver = (GatewayMacResolver)impl;