Merge "Bug 4734 - netvirt/GatewayMacResolverService: null pinter exception"
[ovsdb.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / impl / NeutronL3Adapter.java
index d0fb583268d6346a360e173bab260be1d88cfd82..62a7987ae8ab3e66c20031c636e3e4b6439df2e9 100644 (file)
@@ -1,47 +1,74 @@
 /*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (c) 2014, 2015 Red Hat, Inc. and others. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- * Authors : Dave Tucker, Flavio Fernandes
  */
+
 package org.opendaylight.ovsdb.openstack.netvirt.impl;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
-import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
-import org.opendaylight.neutron.spi.INeutronPortCRUD;
-import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
-import org.opendaylight.neutron.spi.NeutronFloatingIP;
-import org.opendaylight.neutron.spi.NeutronNetwork;
-import org.opendaylight.neutron.spi.NeutronPort;
-import org.opendaylight.neutron.spi.NeutronRouter;
-import org.opendaylight.neutron.spi.NeutronRouter_Interface;
-import org.opendaylight.neutron.spi.NeutronSubnet;
-import org.opendaylight.neutron.spi.Neutron_IPs;
 import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
-import org.opendaylight.ovsdb.openstack.netvirt.api.*;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
+import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
+import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Status;
+import org.opendaylight.ovsdb.openstack.netvirt.api.StatusCode;
+import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronPort;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter_Interface;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSubnet;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronFloatingIPCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronNetworkCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronPortCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronSubnetCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.iaware.impl.NeutronIAwareUtil;
+import org.opendaylight.ovsdb.utils.neutron.utils.NeutronModelsDataStoreHelper;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-
-import com.google.common.base.Preconditions;
-import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 
 /**
  * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
@@ -49,7 +76,7 @@ import java.util.Set;
  * as well as the multi-tenant router forwarding provider.
  */
 public class NeutronL3Adapter implements ConfigInterface {
-    private static final Logger LOGGER = LoggerFactory.getLogger(NeutronL3Adapter.class);
+    private static final Logger LOG = LoggerFactory.getLogger(NeutronL3Adapter.class);
 
     // The implementation for each of these services is resolved by the OSGi Service Manager
     private volatile ConfigurationService configurationService;
@@ -58,19 +85,27 @@ public class NeutronL3Adapter implements ConfigInterface {
     private volatile INeutronNetworkCRUD neutronNetworkCache;
     private volatile INeutronSubnetCRUD neutronSubnetCache;
     private volatile INeutronPortCRUD neutronPortCache;
+    private volatile INeutronFloatingIPCRUD neutronFloatingIpCache;
     private volatile L3ForwardingProvider l3ForwardingProvider;
     private volatile InboundNatProvider inboundNatProvider;
     private volatile OutboundNatProvider outboundNatProvider;
     private volatile ArpProvider arpProvider;
     private volatile RoutingProvider routingProvider;
+    private volatile GatewayMacResolver gatewayMacResolver;
+    private volatile SecurityServicesManager securityServicesManager;
 
     private class FloatIpData {
-        private final Long dpid;          // br-int of node where floating ip is associated with tenant port
-        private final Long ofPort;        // patch port in br-int used to reach br-ex
-        private final String segId;       // segmentation id of the net where fixed ip is instantiated
-        private final String macAddress;  // mac address assigned to neutron port of floating ip
+        // br-int of node where floating ip is associated with tenant port
+        private final Long dpid;
+        // patch port in br-int used to reach br-ex
+        private final Long ofPort;
+        // segmentation id of the net where fixed ip is instantiated
+        private final String segId;
+        // mac address assigned to neutron port of floating ip
+        private final String macAddress;
         private final String floatingIpAddress;
-        private final String fixedIpAddress;  // ip address given to tenant vm
+        // ip address given to tenant vm
+        private final String fixedIpAddress;
         private final String neutronRouterMac;
 
         FloatIpData(final Long dpid, final Long ofPort, final String segId, final String macAddress,
@@ -85,21 +120,21 @@ public class NeutronL3Adapter implements ConfigInterface {
         }
     }
 
-    private Set<String> inboundIpRewriteCache;
-    private Set<String> outboundIpRewriteCache;
-    private Set<String> outboundIpRewriteExclusionCache;
-    private Set<String> routerInterfacesCache;
-    private Set<String> staticArpEntryCache;
-    private Set<String> l3ForwardingCache;
     private Map<String, String> networkIdToRouterMacCache;
     private Map<String, List<Neutron_IPs>> networkIdToRouterIpListCache;
     private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
+
     private Map<String, Pair<Long, Uuid>> neutronPortToDpIdCache;
     private Map<String, FloatIpData> floatIpDataMapCache;
+
     private String externalRouterMac;
     private Boolean enabled = false;
     private Boolean flgDistributedARPEnabled = true;
+    private Boolean isCachePopulationDone = false;
+    private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5);
+
     private Southbound southbound;
+    private NeutronModelsDataStoreHelper neutronModelsDataStoreHelper;
 
     private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
     private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
@@ -107,20 +142,15 @@ public class NeutronL3Adapter implements ConfigInterface {
     private static final String OWNER_FLOATING_IP = "network:floatingip";
     private static final String DEFAULT_EXT_RTR_MAC = "00:00:5E:00:01:01";
 
-    public NeutronL3Adapter() {
-        LOGGER.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass());
+    public NeutronL3Adapter(NeutronModelsDataStoreHelper neutronHelper) {
+        LOG.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass());
+        this.neutronModelsDataStoreHelper = neutronHelper;
     }
 
     private void initL3AdapterMembers() {
         Preconditions.checkNotNull(configurationService);
 
         if (configurationService.isL3ForwardingEnabled()) {
-            this.inboundIpRewriteCache = new HashSet<>();
-            this.outboundIpRewriteCache = new HashSet<>();
-            this.outboundIpRewriteExclusionCache = new HashSet<>();
-            this.routerInterfacesCache = new HashSet<>();
-            this.staticArpEntryCache = new HashSet<>();
-            this.l3ForwardingCache = new HashSet<>();
             this.networkIdToRouterMacCache = new HashMap<>();
             this.networkIdToRouterIpListCache = new HashMap<>();
             this.subnetIdToRouterInterfaceCache = new HashMap<>();
@@ -133,22 +163,170 @@ public class NeutronL3Adapter implements ConfigInterface {
             }
 
             this.enabled = true;
-            LOGGER.info("OVSDB L3 forwarding is enabled");
+            LOG.info("OVSDB L3 forwarding is enabled");
             if (configurationService.isDistributedArpDisabled()) {
                 this.flgDistributedARPEnabled = false;
-                LOGGER.info("Distributed ARP responder is disabled");
+                LOG.info("Distributed ARP responder is disabled");
             } else {
-                LOGGER.debug("Distributed ARP responder is enabled");
+                LOG.debug("Distributed ARP responder is enabled");
             }
         } else {
-            LOGGER.debug("OVSDB L3 forwarding is disabled");
+            LOG.debug("OVSDB L3 forwarding is disabled");
         }
     }
 
-    //
-    // Callbacks from OVSDB's northbound handlers
-    //
 
+    private void populateL3ForwardingCaches() {
+        if (!this.enabled) {
+            return;
+        }
+        if(this.isCachePopulationDone || this.neutronFloatingIpCache == null
+                || this.neutronPortCache == null ||this.neutronNetworkCache == null) {
+            return;
+        }
+        this.isCachePopulationDone = true;
+        LOG.debug("Populating NetVirt L3 caches from data store configuration");
+        Routers routers = this.neutronModelsDataStoreHelper.readAllNeutronRouters();
+        Ports ports = this.neutronModelsDataStoreHelper.readAllNeutronPorts();
+        if(routers != null && routers.getRouter() != null && ports != null) {
+            LOG.debug("L3 Cache Population : {} Neutron router present in data store",routers.getRouter().size());
+            for( Router router : routers.getRouter()) {
+                LOG.debug("L3 Cache Population : Populate caches for router {}",router);
+                if(!ports.getPort().isEmpty()) {
+                    for( Port port : ports.getPort()) {
+                        if (port.getDeviceId().equals(router.getUuid().getValue()) &&
+                                port.getDeviceOwner().equals(OWNER_ROUTER_INTERFACE)) {
+                            LOG.debug("L3 Cache Population : Router interface {} found.",port);
+                            networkIdToRouterMacCache.put(port.getNetworkId().getValue()
+                                    , port.getMacAddress());
+
+                            networkIdToRouterIpListCache.put(port.getNetworkId().getValue(),
+                                    NeutronIAwareUtil.convertMDSalIpToNeutronIp(port.getFixedIps()));
+                            subnetIdToRouterInterfaceCache.put(port.getFixedIps().get(0).getSubnetId().getValue(),
+                                    NeutronIAwareUtil.convertMDSalInterfaceToNeutronRouterInterface(port));
+                        }
+                    }
+                }else {
+                    LOG.warn("L3 Cache Population :Did not find any port information " +
+                            "in config Data Store for router {}",router);
+                }
+            }
+        }
+        LOG.debug("NetVirt L3 caches population is done");
+    }
+
+    private Pair<Long, Uuid> getDpIdOfNeutronPort(String neutronTenantPortUuid) {
+        if(neutronPortToDpIdCache.get(neutronTenantPortUuid) == null) {
+            List<Node> bridges = this.southbound.readOvsdbTopologyBridgeNodes();
+            LOG.debug("getDpIdOfNeutronPort : {} bridges present in ovsdb topology",bridges.size());
+            for(Node bridge : bridges) {
+                List<OvsdbTerminationPointAugmentation> interfaces
+                        = southbound.extractTerminationPointAugmentations(bridge);
+                if(interfaces != null && !interfaces.isEmpty()) {
+                    LOG.debug("getDpIdOfNeutronPort : {} termination point present on bridge {}",
+                            interfaces.size(), bridge.getNodeId());
+                    for (OvsdbTerminationPointAugmentation intf : interfaces) {
+                        NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
+                        if(neutronPort != null && neutronPort.getID().equals(neutronTenantPortUuid)) {
+                            Long dpId = getDpidForIntegrationBridge(bridge);
+                            Uuid interfaceUuid = intf.getInterfaceUuid();
+                            LOG.debug("getDpIdOfNeutronPort : Found bridge {} and interface {} for the tenant neutron" +
+                                    " port {}",dpId,interfaceUuid,neutronTenantPortUuid);
+                            handleInterfaceEventAdd(neutronPort.getPortUUID(), dpId, interfaceUuid);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return neutronPortToDpIdCache.get(neutronTenantPortUuid);
+    }
+
+    private Collection<FloatIpData> getAllFloatingIPsWithMetadata() {
+        LOG.debug("getAllFloatingIPsWithMetadata : Fechting all floating Ips and it's metadata");
+        List<NeutronFloatingIP> neutronFloatingIps = neutronFloatingIpCache.getAllFloatingIPs();
+        if(neutronFloatingIps != null && !neutronFloatingIps.isEmpty()) {
+            for (NeutronFloatingIP neutronFloatingIP : neutronFloatingIps) {
+                if(!floatIpDataMapCache.containsKey(neutronFloatingIP.getID())){
+                    LOG.debug("Metadata for floating ip {} is not present in the cache. " +
+                            "Fetching from data store.",neutronFloatingIP.getID());
+                    this.getFloatingIPWithMetadata(neutronFloatingIP.getID());
+                }
+            }
+        }
+        LOG.debug("getAllFloatingIPsWithMetadata : {} floating points found in data store",floatIpDataMapCache.size());
+        return floatIpDataMapCache.values();
+    }
+    private FloatIpData getFloatingIPWithMetadata(String neutronFloatingId) {
+        LOG.debug("getFloatingIPWithMetadata : Get Floating ip and it's meta data for neutron " +
+                "floating id {} ",neutronFloatingId);
+        if(floatIpDataMapCache.get(neutronFloatingId) == null) {
+            NeutronFloatingIP neutronFloatingIP = neutronFloatingIpCache.getFloatingIP(neutronFloatingId);
+            if (neutronFloatingIP == null) {
+                LOG.error("getFloatingIPWithMetadata : Floating ip {} is missing from data store, that should not happen",neutronFloatingId);
+                return null;
+            }
+            List<NeutronPort> neutronPorts = neutronPortCache.getAllPorts();
+            NeutronPort neutronPortForFloatIp = null;
+            for (NeutronPort neutronPort : neutronPorts) {
+                if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
+                        neutronPort.getDeviceID().equals(neutronFloatingIP.getID())) {
+                    neutronPortForFloatIp = neutronPort;
+                    break;
+                }
+            }
+
+            String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
+            if(neutronTenantPortUuid == null) {
+                return null;
+            }
+            Pair<Long, Uuid> nodeIfPair = this.getDpIdOfNeutronPort(neutronTenantPortUuid);
+            String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
+            String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
+            String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
+
+            NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
+            NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
+                    neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
+            String providerSegmentationId = tenantNeutronNetwork != null ?
+                    tenantNeutronNetwork.getProviderSegmentationID() : null;
+            String neutronRouterMac = tenantNeutronNetwork != null ?
+                    networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
+
+            if (nodeIfPair == null || neutronTenantPortUuid == null ||
+                    providerSegmentationId == null || providerSegmentationId.isEmpty() ||
+                    floatingIpMac == null || floatingIpMac.isEmpty() ||
+                    neutronRouterMac == null || neutronRouterMac.isEmpty()) {
+                LOG.debug("getFloatingIPWithMetadata :Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} " +
+                                "seg {} mac {} rtrMac {}",
+                        fixedIpAddress,
+                        floatingIpAddress,
+                        neutronPortForFloatIp,
+                        neutronTenantPortUuid,
+                        providerSegmentationId,
+                        floatingIpMac,
+                        neutronRouterMac);
+
+                return null;
+            }
+
+            // get ofport for patch port in br-int
+            final Long dpId = nodeIfPair.getLeft();
+            final Long ofPort = findOFPortForExtPatch(dpId);
+            if (ofPort == null) {
+                LOG.warn("getFloatingIPWithMetadata : Unable to locate OF port of patch port " +
+                                "to connect floating ip to external bridge. dpid {}",
+                        dpId);
+                return null;
+            }
+
+            final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
+                    floatingIpAddress, fixedIpAddress, neutronRouterMac);
+            floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
+
+        }
+        return floatIpDataMapCache.get(neutronFloatingId);
+    }
     /**
      * Invoked to configure the mac address for the external gateway in br-ex. ovsdb netvirt needs help in getting
      * mac for given ip in br-ex (bug 3378). For example, since ovsdb has no real arp, it needs a service in can
@@ -171,10 +349,7 @@ public class NeutronL3Adapter implements ConfigInterface {
      * @param subnet An instance of NeutronSubnet object.
      */
     public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
-        LOGGER.debug("Neutron subnet {} event : {}", action, subnet.toString());
-        if (!this.enabled) {
-            return;
-        }
+        LOG.debug("Neutron subnet {} event : {}", action, subnet.toString());
     }
 
     /**
@@ -186,25 +361,55 @@ public class NeutronL3Adapter implements ConfigInterface {
      * @param neutronPort An instance of NeutronPort object.
      */
     public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
-        LOGGER.debug("Neutron port {} event : {}", action, neutronPort.toString());
+        LOG.debug("Neutron port {} event : {}", action, neutronPort.toString());
+
+        this.processSecurityGroupUpdate(neutronPort);
         if (!this.enabled) {
             return;
         }
 
         final boolean isDelete = action == Action.DELETE;
 
+        if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
+            if(!isDelete){
+                Node externalBridgeNode = getExternalBridgeNode();
+                if(externalBridgeNode != null){
+                    LOG.info("Port {} is network router gateway interface, "
+                            + "triggering gateway resolution for the attached external network on node {}", neutronPort, externalBridgeNode);
+                    this.triggerGatewayMacResolver(externalBridgeNode, neutronPort);
+                }else{
+                    LOG.error("Did not find Node that has external bridge (br-ex), Gateway resolution failed");
+                }
+            }else{
+                NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
+
+                if (externalNetwork != null && externalNetwork.isRouterExternal()) {
+                    final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
+                    // TODO support IPv6
+                    if (externalSubnet != null &&
+                            externalSubnet.getIpVersion() == 4) {
+                        gatewayMacResolver.stopPeriodicRefresh(new Ipv4Address(externalSubnet.getGatewayIP()));
+                    }
+                }
+            }
+        }
+
         // Treat the port event as a router interface event if the port belongs to router. This is a
         // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
         //
         if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
             neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
-            for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
-                NeutronRouter_Interface neutronRouterInterface =
+
+            if (neutronPort.getFixedIPs() != null) {
+                for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
+                    NeutronRouter_Interface neutronRouterInterface =
                         new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
-                neutronRouterInterface.setID(neutronIP.getSubnetUUID());  // id of router interface to be same as subnet
-                neutronRouterInterface.setTenantID(neutronPort.getTenantID());
+                    // id of router interface to be same as subnet
+                    neutronRouterInterface.setID(neutronIP.getSubnetUUID());
+                    neutronRouterInterface.setTenantID(neutronPort.getTenantID());
 
-                this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
+                    this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
+                }
             }
         } else {
             // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
@@ -212,7 +417,7 @@ public class NeutronL3Adapter implements ConfigInterface {
             // need to do this check here because a router interface is not added to a node until tenant becomes needed
             // there.
             //
-            if (!isDelete) {
+            if (!isDelete && neutronPort.getFixedIPs() != null) {
                 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
                     NeutronRouter_Interface neutronRouterInterface =
                             subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
@@ -232,10 +437,7 @@ public class NeutronL3Adapter implements ConfigInterface {
      * @param neutronRouter An instance of NeutronRouter object.
      */
     public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
-        LOGGER.debug("Neutron router {} event : {}", action, neutronRouter.toString());
-        if (!this.enabled) {
-            return;
-        }
+        LOG.debug("Neutron router {} event : {}", action, neutronRouter.toString());
     }
 
     /**
@@ -249,7 +451,7 @@ public class NeutronL3Adapter implements ConfigInterface {
     public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
                                                   final NeutronRouter_Interface neutronRouterInterface,
                                                   Action action) {
-        LOGGER.debug("Router interface {} got event {}. Subnet {}",
+        LOG.debug("Router interface {} got event {}. Subnet {}",
                      neutronRouterInterface.getPortUUID(),
                      action,
                      neutronRouterInterface.getSubnetUUID());
@@ -268,15 +470,24 @@ public class NeutronL3Adapter implements ConfigInterface {
             boolean currPortShouldBeDeleted = false;
             // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
             if (isDelete) {
-                for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
-                    if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
-                        currPortShouldBeDeleted = true;
-                        break;
+                if (neutronPort.getFixedIPs() != null) {
+                    for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
+                        if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
+                            currPortShouldBeDeleted = true;
+                            break;
+                        }
                     }
                 }
             }
             this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
         }
+
+        if (isDelete) {
+            /*
+             *  Bug 4277: Remove the router interface cache only after deleting the neutron port l3 flows.
+             */
+            this.cleanupRouterCache(neutronRouterInterface);
+        }
     }
 
     /**
@@ -286,13 +497,13 @@ public class NeutronL3Adapter implements ConfigInterface {
      * way around) on OpenFlow Table 100.
      *
      * @param actionIn the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
-     * @param neutronFloatingIP An {@link org.opendaylight.neutron.spi.NeutronFloatingIP} instance of NeutronFloatingIP object.
+     * @param neutronFloatingIP An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP} instance of NeutronFloatingIP object.
      */
     public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
                                              Action actionIn) {
         Preconditions.checkNotNull(neutronFloatingIP);
 
-        LOGGER.debug(" Floating IP {} {}<->{}, network uuid {}", actionIn,
+        LOG.debug(" Floating IP {} {}<->{}, network uuid {}", actionIn,
                 neutronFloatingIP.getFixedIPAddress(),
                 neutronFloatingIP.getFloatingIPAddress(),
                 neutronFloatingIP.getFloatingNetworkUUID());
@@ -313,7 +524,8 @@ public class NeutronL3Adapter implements ConfigInterface {
         // this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
 
         if (action != Action.DELETE) {
-            programFlowsForFloatingIPArpAdd(neutronFloatingIP);  // must be first, as it updates floatIpDataMapCache
+            // must be first, as it updates floatIpDataMapCache
+            programFlowsForFloatingIPArpAdd(neutronFloatingIP);
 
             programFlowsForFloatingIPInbound(neutronFloatingIP, Action.ADD);
             programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.ADD);
@@ -321,7 +533,8 @@ public class NeutronL3Adapter implements ConfigInterface {
             programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.DELETE);
             programFlowsForFloatingIPInbound(neutronFloatingIP, Action.DELETE);
 
-            programFlowsForFloatingIPArpDelete(neutronFloatingIP.getID()); // must be last, as it updates floatIpDataMapCache
+            // must be last, as it updates floatIpDataMapCache
+            programFlowsForFloatingIPArpDelete(neutronFloatingIP.getID());
         }
     }
 
@@ -332,9 +545,9 @@ public class NeutronL3Adapter implements ConfigInterface {
     private void programFlowsForFloatingIPInbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
         Preconditions.checkNotNull(neutronFloatingIP);
 
-        final FloatIpData fid = floatIpDataMapCache.get(neutronFloatingIP.getID());
+        final FloatIpData fid = getFloatingIPWithMetadata(neutronFloatingIP.getID());
         if (fid == null) {
-            LOGGER.trace("programFlowsForFloatingIPInboundAdd {} for {} uuid {} not in local cache",
+            LOG.trace("programFlowsForFloatingIPInboundAdd {} for {} uuid {} not in local cache",
                     action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
             return;
         }
@@ -349,9 +562,9 @@ public class NeutronL3Adapter implements ConfigInterface {
     private void programFlowsForFloatingIPOutbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
         Preconditions.checkNotNull(neutronFloatingIP);
 
-        final FloatIpData fid = floatIpDataMapCache.get(neutronFloatingIP.getID());
+        final FloatIpData fid = getFloatingIPWithMetadata(neutronFloatingIP.getID());
         if (fid == null) {
-            LOGGER.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache",
+            LOG.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache",
                     action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
             return;
         }
@@ -359,13 +572,13 @@ public class NeutronL3Adapter implements ConfigInterface {
     }
 
     private void flushExistingIpRewrite() {
-        for (FloatIpData fid : floatIpDataMapCache.values()) {
+        for (FloatIpData fid : getAllFloatingIPsWithMetadata()) {
             programOutboundIpRewriteStage1(fid, Action.DELETE);
         }
     }
 
     private void rebuildExistingIpRewrite() {
-        for (FloatIpData fid : floatIpDataMapCache.values()) {
+        for (FloatIpData fid : getAllFloatingIPsWithMetadata()) {
             programOutboundIpRewriteStage1(fid, Action.ADD);
         }
     }
@@ -380,16 +593,10 @@ public class NeutronL3Adapter implements ConfigInterface {
         Preconditions.checkNotNull(neutronFloatingIP.getFixedIPAddress());
         Preconditions.checkNotNull(neutronFloatingIP.getFloatingIPAddress());
 
-        if (floatIpDataMapCache.get(neutronFloatingIP.getID()) != null) {
-            LOGGER.trace("programFlowsForFloatingIPArpAdd for neutronFloatingIP {} uuid {} is already done",
-                    neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
-            return;
-        }
-
         // find bridge Node where floating ip is configured by looking up cache for its port
         final NeutronPort neutronPortForFloatIp = findNeutronPortForFloatingIp(neutronFloatingIP.getID());
         final String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
-        final Pair<Long, Uuid> nodeIfPair = neutronPortToDpIdCache.get(neutronTenantPortUuid);
+        final Pair<Long, Uuid> nodeIfPair = this.getDpIdOfNeutronPort(neutronTenantPortUuid);
         final String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
         final String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
         final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
@@ -406,7 +613,7 @@ public class NeutronL3Adapter implements ConfigInterface {
                 providerSegmentationId == null || providerSegmentationId.isEmpty() ||
                 floatingIpMac == null || floatingIpMac.isEmpty() ||
                 neutronRouterMac == null || neutronRouterMac.isEmpty()) {
-            LOGGER.trace("Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} seg {} mac {} rtrMac {}",
+            LOG.trace("Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} seg {} mac {} rtrMac {}",
                     fixedIpAddress,
                     floatingIpAddress,
                     neutronPortForFloatIp,
@@ -421,7 +628,7 @@ public class NeutronL3Adapter implements ConfigInterface {
         final Long dpId = nodeIfPair.getLeft();
         final Long ofPort = findOFPortForExtPatch(dpId);
         if (ofPort == null) {
-            LOGGER.warn("Unable to locate OF port of patch port to connect floating ip to external bridge. dpid {}",
+            LOG.warn("Unable to locate OF port of patch port to connect floating ip to external bridge. dpid {}",
                     dpId);
             return;
         }
@@ -433,28 +640,28 @@ public class NeutronL3Adapter implements ConfigInterface {
             final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
                     floatingIpAddress, fixedIpAddress, neutronRouterMac);
             floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
-            LOGGER.info("Floating IP {}<->{} programmed ARP mac {} on OFport {} seg {} dpid {}",
+            LOG.info("Floating IP {}<->{} programmed ARP mac {} on OFport {} seg {} dpid {}",
                     neutronFloatingIP.getFixedIPAddress(), neutronFloatingIP.getFloatingIPAddress(),
                     floatingIpMac, ofPort, providerSegmentationId, dpId);
         }
     }
 
     private void programFlowsForFloatingIPArpDelete(final String neutronFloatingIPUuid) {
-        final FloatIpData floatIpData = floatIpDataMapCache.get(neutronFloatingIPUuid);
+        final FloatIpData floatIpData = getFloatingIPWithMetadata(neutronFloatingIPUuid);
         if (floatIpData == null) {
-            LOGGER.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid);
+            LOG.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid);
             return;
         }
 
         if (programStaticArpStage1(floatIpData.dpid, encodeExcplicitOFPort(floatIpData.ofPort), floatIpData.macAddress,
                 floatIpData.floatingIpAddress, Action.DELETE)) {
             floatIpDataMapCache.remove(neutronFloatingIPUuid);
-            LOGGER.info("Floating IP {} un-programmed ARP mac {} on {} dpid {}",
+            LOG.info("Floating IP {} un-programmed ARP mac {} on {} dpid {}",
                     floatIpData.floatingIpAddress, floatIpData.macAddress, floatIpData.ofPort, floatIpData.dpid);
         }
     }
 
-    private final NeutronPort findNeutronPortForFloatingIp(final String floatingIpUuid) {
+    private NeutronPort findNeutronPortForFloatingIp(final String floatingIpUuid) {
         for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
             if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
                     neutronPort.getDeviceID().equals(floatingIpUuid)) {
@@ -464,7 +671,7 @@ public class NeutronL3Adapter implements ConfigInterface {
         return null;
     }
 
-    private final Long findOFPortForExtPatch(Long dpId) {
+    private Long findOFPortForExtPatch(Long dpId) {
         final String brInt = configurationService.getIntegrationBridgeName();
         final String brExt = configurationService.getExternalBridgeName();
         final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
@@ -472,7 +679,7 @@ public class NeutronL3Adapter implements ConfigInterface {
         Preconditions.checkNotNull(dpId);
         Preconditions.checkNotNull(portNameInt);
 
-        final long dpidPrimitive = dpId.longValue();
+        final long dpidPrimitive = dpId;
         for (Node node : nodeCacheManager.getBridgeNodes()) {
             if (dpidPrimitive == southbound.getDataPathId(node)) {
                 final OvsdbTerminationPointAugmentation terminationPointOfBridge =
@@ -487,13 +694,10 @@ public class NeutronL3Adapter implements ConfigInterface {
      * Process the event.
      *
      * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
-     * @param neutronNetwork An {@link org.opendaylight.neutron.spi.NeutronNetwork} instance of NeutronFloatingIP object.
+     * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronFloatingIP object.
      */
     public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
-        LOGGER.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
-        if (!this.enabled) {
-            return;
-        }
+        LOG.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
     }
 
     //
@@ -505,13 +709,13 @@ public class NeutronL3Adapter implements ConfigInterface {
      * @param bridgeNode An instance of Node object.
      * @param intf An {@link org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105
      * .OvsdbTerminationPointAugmentation} instance of OvsdbTerminationPointAugmentation object.
-     * @param neutronNetwork An {@link org.opendaylight.neutron.spi.NeutronNetwork} instance of NeutronNetwork
+     * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronNetwork
      * object.
      * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
      */
     public void handleInterfaceEvent(final Node bridgeNode, final OvsdbTerminationPointAugmentation intf,
                                      final NeutronNetwork neutronNetwork, Action action) {
-        LOGGER.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
+        LOG.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
                      action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork);
         if (!this.enabled) {
             return;
@@ -521,14 +725,13 @@ public class NeutronL3Adapter implements ConfigInterface {
         final Long dpId = getDpidForIntegrationBridge(bridgeNode);
         final Uuid interfaceUuid = intf.getInterfaceUuid();
 
-        LOGGER.trace("southbound interface {} node:{} interface:{}, neutronNetwork:{} port:{} dpid:{} intfUuid:{}",
+        LOG.trace("southbound interface {} node:{} interface:{}, neutronNetwork:{} port:{} dpid:{} intfUuid:{}",
                 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork, neutronPort, dpId, interfaceUuid);
 
         if (neutronPort != null) {
             final String neutronPortUuid = neutronPort.getPortUUID();
 
-            if (action != Action.DELETE && neutronPortToDpIdCache.get(neutronPortUuid) == null &&
-                    dpId != null && interfaceUuid != null) {
+            if (action != Action.DELETE && dpId != null && interfaceUuid != null) {
                 handleInterfaceEventAdd(neutronPortUuid, dpId, interfaceUuid);
             }
 
@@ -542,7 +745,7 @@ public class NeutronL3Adapter implements ConfigInterface {
 
     private void handleInterfaceEventAdd(final String neutronPortUuid, Long dpId, final Uuid interfaceUuid) {
         neutronPortToDpIdCache.put(neutronPortUuid, new ImmutablePair<>(dpId, interfaceUuid));
-        LOGGER.debug("handleInterfaceEvent add cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
+        LOG.debug("handleInterfaceEvent add cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
                 neutronPortUuid, dpId, interfaceUuid.getValue());
     }
 
@@ -551,7 +754,7 @@ public class NeutronL3Adapter implements ConfigInterface {
         for (Map.Entry<String, Pair<Long, Uuid>> entry : neutronPortToDpIdCache.entrySet()) {
             final String currPortUuid = entry.getKey();
             if (intf.getInterfaceUuid().equals(entry.getValue().getRight())) {
-                LOGGER.debug("handleInterfaceEventDelete remove cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
+                LOG.debug("handleInterfaceEventDelete remove cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
                         currPortUuid, dpId, intf.getInterfaceUuid().getValue());
                 neutronPortToDpIdCache.remove(currPortUuid);
                 break;
@@ -592,14 +795,16 @@ public class NeutronL3Adapter implements ConfigInterface {
         final Action action = isDelete ? Action.DELETE : Action.ADD;
         List<Node> nodes = nodeCacheManager.getBridgeNodes();
         if (nodes.isEmpty()) {
-            LOGGER.trace("updateL3ForNeutronPort has no nodes to work with");
+            LOG.trace("updateL3ForNeutronPort has no nodes to work with");
         }
         for (Node node : nodes) {
             final Long dpid = getDpidForIntegrationBridge(node);
             if (dpid == null) {
                 continue;
             }
-
+            if (neutronPort.getFixedIPs() == null) {
+                continue;
+            }
             for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
                 final String tenantIpStr = neutronIP.getIpAddress();
                 if (tenantIpStr.isEmpty()) {
@@ -611,43 +816,71 @@ public class NeutronL3Adapter implements ConfigInterface {
                 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
 
                 // Configure distributed ARP responder
-                if (true == flgDistributedARPEnabled) {
+                if (flgDistributedARPEnabled) {
                     programStaticArpStage1(dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
                 }
             }
         }
     }
 
+    private void processSecurityGroupUpdate(NeutronPort neutronPort) {
+        LOG.trace("processSecurityGroupUpdate:" + neutronPort);
+        /**
+         * Get updated data and original data for the the changed. Identify the security groups that got
+         * added and removed and call the appropriate providers for updating the flows.
+         */
+        try {
+            NeutronPort originalPort = neutronPort.getOriginalPort();
+            if (null == originalPort) {
+                LOG.debug("processSecurityGroupUpdate: originalport is empty");
+                return;
+            }
+            List<NeutronSecurityGroup> addedGroup = getsecurityGroupChanged(neutronPort,
+                                                                            neutronPort.getOriginalPort());
+            List<NeutronSecurityGroup> deletedGroup = getsecurityGroupChanged(neutronPort.getOriginalPort(),
+                                                                              neutronPort);
+
+            if (null != addedGroup && !addedGroup.isEmpty()) {
+                securityServicesManager.syncSecurityGroup(neutronPort,addedGroup,true);
+            }
+            if (null != deletedGroup && !deletedGroup.isEmpty()) {
+                securityServicesManager.syncSecurityGroup(neutronPort,deletedGroup,false);
+            }
+
+        } catch (Exception e) {
+            LOG.error("Exception in processSecurityGroupUpdate", e);
+        }
+    }
+
+    private List<NeutronSecurityGroup> getsecurityGroupChanged(NeutronPort port1, NeutronPort port2) {
+        LOG.trace("getsecurityGroupChanged:" + "Port1:" + port1 + "Port2" + port2);
+        List<NeutronSecurityGroup> list1 = new ArrayList<>(port1.getSecurityGroups());
+        List<NeutronSecurityGroup> list2 = new ArrayList<>(port2.getSecurityGroups());
+        for (Iterator<NeutronSecurityGroup> iterator = list1.iterator(); iterator.hasNext();) {
+            NeutronSecurityGroup securityGroup1 = iterator.next();
+            for (NeutronSecurityGroup securityGroup2 :list2) {
+                if (securityGroup1.getID().equals(securityGroup2.getID())) {
+                    iterator.remove();
+                }
+            }
+        }
+        return list1;
+    }
+
     private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
                                            String macAddress, String ipStr,
                                            Action actionForNode) {
-        // Based on the local cache, figure out whether programming needs to occur. To do this, we
-        // will look at desired action for node.
-        //
-        final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + ipStr;
-        final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
-
-        if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
-            LOGGER.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
+        if (actionForNode == Action.DELETE) {
+            LOG.trace("Deleting Flow : programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {}",
                          node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
-            return;
         }
-        if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
-            LOGGER.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
+        if (actionForNode == Action.ADD) {
+            LOG.trace("Adding Flow : programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {}",
                     node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
-            return;
         }
 
-        Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
+        this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
                                                        macAddress, ipStr, actionForNode);
-        if (status.isSuccess()) {
-            // Update cache
-            if (actionForNode == Action.ADD) {
-                l3ForwardingCache.add(cacheKey);
-            } else {
-                l3ForwardingCache.remove(cacheKey);
-            }
-        }
     }
 
     private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
@@ -666,11 +899,11 @@ public class NeutronL3Adapter implements ConfigInterface {
         }
 
         if (status.isSuccess()) {
-            LOGGER.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
+            LOG.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
                          l3ForwardingProvider == null ? "skipped" : "programmed",
                          macAddress, address, node.getNodeId().getValue(), actionForNode);
         } else {
-            LOGGER.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
+            LOG.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
                          macAddress, address, node.getNodeId().getValue(), actionForNode, status);
         }
         return status;
@@ -690,12 +923,11 @@ public class NeutronL3Adapter implements ConfigInterface {
                                               neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
         final String destinationSegmentationId = neutronNetwork != null ?
                                                  neutronNetwork.getProviderSegmentationID() : null;
-        final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
         final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
         final String cidr = subnet != null ? subnet.getCidr() : null;
         final int mask = getMaskLenFromCidr(cidr);
 
-        LOGGER.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
+        LOG.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
                      destNeutronRouterInterface, isDelete);
 
         // in delete path, mac address as well as ip address are not provided. Being so, let's find them from
@@ -713,7 +945,7 @@ public class NeutronL3Adapter implements ConfigInterface {
             cidr == null || cidr.isEmpty() ||
             macAddress == null || macAddress.isEmpty() ||
             ipList == null || ipList.isEmpty()) {
-            LOGGER.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{}  ip:{}",
+            LOG.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{}  ip:{}",
                          destinationSegmentationId, cidr, macAddress, ipList);
             // done: go no further w/out all the info needed...
             return;
@@ -731,7 +963,7 @@ public class NeutronL3Adapter implements ConfigInterface {
 
         List<Node> nodes = nodeCacheManager.getBridgeNodes();
         if (nodes.isEmpty()) {
-            LOGGER.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
+            LOG.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
         }
         for (Node node : nodes) {
             final Long dpid = getDpidForIntegrationBridge(node);
@@ -742,7 +974,7 @@ public class NeutronL3Adapter implements ConfigInterface {
             for (Neutron_IPs neutronIP : ipList) {
                 final String ipStr = neutronIP.getIpAddress();
                 if (ipStr.isEmpty()) {
-                    LOGGER.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
+                    LOG.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
                             node.getNodeId().getValue(), ipStr);
                     continue;
                 }
@@ -774,13 +1006,7 @@ public class NeutronL3Adapter implements ConfigInterface {
             }
         }
 
-        // Keep cache for finding router's mac from network uuid -- remove
-        //
-        if (isDelete) {
-            networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
-            networkIdToRouterIpListCache.remove(neutronNetwork.getNetworkUUID());
-            subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
-        }
+        // Keep cache for finding router's mac from network uuid -- NOTE: remove is done later, via cleanupRouterCache()
     }
 
     private void programFlowForNetworkFromExternal(final Node node,
@@ -810,7 +1036,7 @@ public class NeutronL3Adapter implements ConfigInterface {
 
         final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
         if (sourceSubnetId == null) {
-            LOGGER.error("Could not get provider Subnet ID from router interface {}",
+            LOG.error("Could not get provider Subnet ID from router interface {}",
                          srcNeutronRouterInterface.getID());
             return;
         }
@@ -818,13 +1044,13 @@ public class NeutronL3Adapter implements ConfigInterface {
         final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
         final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
         if (sourceNetworkId == null) {
-            LOGGER.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
+            LOG.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
             return;
         }
 
         final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
         if (sourceNetwork == null) {
-            LOGGER.error("Could not get provider Network for Network ID {}", sourceNetworkId);
+            LOG.error("Could not get provider Network for Network ID {}", sourceNetworkId);
             return;
         }
 
@@ -834,7 +1060,7 @@ public class NeutronL3Adapter implements ConfigInterface {
         }
         final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
         if (sourceSegmentationId == null) {
-            LOGGER.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
+            LOG.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
             return;
         }
         if (sourceSegmentationId.equals(destinationSegmentationId)) {
@@ -856,7 +1082,7 @@ public class NeutronL3Adapter implements ConfigInterface {
             if (cidr2 == null || cidr2.isEmpty() ||
                 macAddress2 == null || macAddress2.isEmpty() ||
                 ipList2 == null || ipList2.isEmpty()) {
-                LOGGER.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
+                LOG.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
                              sourceSegmentationId, cidr2, macAddress2, ipList2);
                 // done: go no further w/out all the info needed...
                 return;
@@ -880,41 +1106,22 @@ public class NeutronL3Adapter implements ConfigInterface {
                                               String destinationSegmentationId,
                                               String macAddress, String ipStr, int mask,
                                               Action actionForNode) {
-        // Based on the local cache, figure out whether programming needs to occur. To do this, we
-        // will look at desired action for node.
-        //
-        final String cacheKey = node.getNodeId().getValue() + ":" +
-                                sourceSegmentationId + ":" + destinationSegmentationId + ":" +
-                                ipStr + "/" + Integer.toString(mask);
-        final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
-
-        if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
-            LOGGER.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
-                         " action {} is already done",
+        if (actionForNode == Action.DELETE) {
+            LOG.trace("Deleting Flow : programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
+                         " action {}",
                          node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
                          macAddress, ipStr, mask, actionForNode);
             return;
         }
-        if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
-            LOGGER.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
-                         " action {} is already done",
+        if (actionForNode == Action.ADD) {
+            LOG.trace("Adding Flow : programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
+                         " action {}",
                          node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
                          macAddress, ipStr, mask, actionForNode);
-            return;
         }
 
-        Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
+        this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
                                                           macAddress, ipStr, mask, actionForNode);
-        if (status.isSuccess()) {
-            // Update cache
-            if (actionForNode == Action.ADD) {
-                // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
-                routerInterfacesCache.add(cacheKey);
-            } else {
-                // TODO: multiTenantAwareRouter.removeInterface(...);
-                routerInterfacesCache.remove(cacheKey);
-            }
-        }
     }
 
     private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
@@ -934,12 +1141,12 @@ public class NeutronL3Adapter implements ConfigInterface {
         }
 
         if (status.isSuccess()) {
-            LOGGER.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
+            LOG.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
                          routingProvider == null ? "skipped" : "programmed",
                          macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
                          actionForNode);
         } else {
-            LOGGER.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
+            LOG.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
                          macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
                          actionForNode, status);
         }
@@ -949,34 +1156,17 @@ public class NeutronL3Adapter implements ConfigInterface {
     private boolean programStaticArpStage1(Long dpid, String segOrOfPort,
                                            String macAddress, String ipStr,
                                            Action action) {
-        // Based on the local cache, figure out whether programming needs to occur. To do this, we
-        // will look at desired action for node.
-        //
-        final String cacheKey = dpid + ":" + segOrOfPort + ":" + ipStr;
-        final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
-
-        if (action == Action.DELETE && isProgrammed == Boolean.FALSE) {
-            LOGGER.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
+        if (action == Action.DELETE ) {
+            LOG.trace("Deleting Flow : programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {}",
                     dpid, segOrOfPort, macAddress, ipStr, action);
-            return true;
         }
-        if (action == Action.ADD && isProgrammed == Boolean.TRUE) {
-            LOGGER.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
+        if (action == Action.ADD) {
+            LOG.trace("Adding Flow : programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
                     dpid, segOrOfPort, macAddress, ipStr, action);
-            return true;
         }
 
         Status status = this.programStaticArpStage2(dpid, segOrOfPort, macAddress, ipStr, action);
-        if (status.isSuccess()) {
-            // Update cache
-            if (action == Action.ADD) {
-                staticArpEntryCache.add(cacheKey);
-            } else {
-                staticArpEntryCache.remove(cacheKey);
-            }
-            return true;
-        }
-        return false;
+        return status.isSuccess();
     }
 
     private Status programStaticArpStage2(Long dpid,
@@ -996,11 +1186,11 @@ public class NeutronL3Adapter implements ConfigInterface {
         }
 
         if (status.isSuccess()) {
-            LOGGER.debug("ProgramStaticArp {} for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{}",
+            LOG.debug("ProgramStaticArp {} for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{}",
                          arpProvider == null ? "skipped" : "programmed",
                          macAddress, address, dpid, segOrOfPort, action);
         } else {
-            LOGGER.error("ProgramStaticArp failed for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{} status:{}",
+            LOG.error("ProgramStaticArp failed for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{} status:{}",
                          macAddress, address, dpid, segOrOfPort, action, status);
         }
         return status;
@@ -1009,37 +1199,20 @@ public class NeutronL3Adapter implements ConfigInterface {
     private boolean programInboundIpRewriteStage1(Long dpid, Long inboundOFPort, String providerSegmentationId,
                                                   String matchAddress, String rewriteAddress,
                                                   Action action) {
-        // Based on the local cache, figure out whether programming needs to occur. To do this, we
-        // will look at desired action for node.
-        //
-        final String cacheKey = dpid + ":" + inboundOFPort + ":" + providerSegmentationId + ":" + matchAddress;
-        final Boolean isProgrammed = inboundIpRewriteCache.contains(cacheKey);
-
-        if (action == Action.DELETE && isProgrammed == Boolean.FALSE) {
-            LOGGER.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
-                    " action {} is already done",
+        if (action == Action.DELETE ) {
+            LOG.trace("Deleting Flow : programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
+                    " action {}",
                     dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
-            return true;
         }
-        if (action == Action.ADD && isProgrammed == Boolean.TRUE) {
-            LOGGER.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
-                    " action is already done",
+        if (action == Action.ADD ) {
+            LOG.trace("Adding Flow : programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
+                    " action {}",
                     dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
-            return true;
         }
 
         Status status = programInboundIpRewriteStage2(dpid, inboundOFPort, providerSegmentationId, matchAddress,
                 rewriteAddress, action);
-        if (status.isSuccess()) {
-            // Update cache
-            if (action == Action.ADD) {
-                inboundIpRewriteCache.add(cacheKey);
-            } else {
-                inboundIpRewriteCache.remove(cacheKey);
-            }
-            return true;
-        }
-        return false;
+        return status.isSuccess();
     }
 
     private Status programInboundIpRewriteStage2(Long dpid, Long inboundOFPort, String providerSegmentationId,
@@ -1060,11 +1233,11 @@ public class NeutronL3Adapter implements ConfigInterface {
 
         if (status.isSuccess()) {
             final boolean isSkipped = inboundNatProvider == null;
-            LOGGER.debug("programInboundIpRewriteStage2 {} for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}",
+            LOG.debug("programInboundIpRewriteStage2 {} for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}",
                     isSkipped ? "skipped" : "programmed",
                     dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
         } else {
-            LOGGER.error("programInboundIpRewriteStage2 failed for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}" +
+            LOG.error("programInboundIpRewriteStage2 failed for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}" +
                          " status:{}",
                     dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action,
                     status);
@@ -1074,33 +1247,16 @@ public class NeutronL3Adapter implements ConfigInterface {
 
     private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId, String cidr,
                                                  Action actionForRewriteExclusion) {
-        // Based on the local cache, figure out whether programming needs to occur. To do this, we
-        // will look at desired action for node.
-        //
-        final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + cidr;
-        final Boolean isProgrammed = outboundIpRewriteExclusionCache.contains(cacheKey);
-
-        if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
-            LOGGER.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done",
+        if (actionForRewriteExclusion == Action.DELETE ) {
+            LOG.trace("Deleting Flow : programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {}",
                          node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
-            return;
         }
-        if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
-            LOGGER.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done",
+        if (actionForRewriteExclusion == Action.ADD) {
+            LOG.trace("Adding Flow : programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {}",
                          node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
-            return;
         }
 
-        Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
-                                                             actionForRewriteExclusion);
-        if (status.isSuccess()) {
-            // Update cache
-            if (actionForRewriteExclusion == Action.ADD) {
-                    outboundIpRewriteExclusionCache.add(cacheKey);
-            } else {
-                    outboundIpRewriteExclusionCache.remove(cacheKey);
-            }
-        }
+        this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,actionForRewriteExclusion);
     }
 
     private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
@@ -1110,45 +1266,28 @@ public class NeutronL3Adapter implements ConfigInterface {
 
         if (status.isSuccess()) {
             final boolean isSkipped = outboundNatProvider == null;
-            LOGGER.debug("IpRewriteExclusion {} for cidr:{} node:{} action:{}",
+            LOG.debug("IpRewriteExclusion {} for cidr:{} node:{} action:{}",
                          isSkipped ? "skipped" : "programmed",
                          cidr, node.getNodeId().getValue(), actionForNode);
         } else {
-            LOGGER.error("IpRewriteExclusion failed for cidr:{} node:{} action:{} status:{}",
+            LOG.error("IpRewriteExclusion failed for cidr:{} node:{} action:{} status:{}",
                          cidr, node.getNodeId().getValue(), actionForNode, status);
         }
         return status;
     }
 
     private void programOutboundIpRewriteStage1(FloatIpData fid, Action action) {
-        // Based on the local cache, figure out whether programming needs to occur. To do this, we
-        // will look at desired action for node.
-        //
-        final String cacheKey = fid.dpid + ":" + fid.segId + ":" + fid.fixedIpAddress;
-        final Boolean isProgrammed = outboundIpRewriteCache.contains(cacheKey);
 
-        if (action == Action.DELETE && isProgrammed == Boolean.FALSE) {
-            LOGGER.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " +
-                         "is already done",
+        if (action == Action.DELETE) {
+            LOG.trace("Deleting Flow : programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} ",
                     fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
-            return;
         }
-        if (action == Action.ADD && isProgrammed == Boolean.TRUE) {
-            LOGGER.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " +
-                         "is already done",
+        if (action == Action.ADD) {
+            LOG.trace("Adding Flow : programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " ,
                     fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
-            return;
         }
 
-        Status status = this.programOutboundIpRewriteStage2(fid, action);
-        if (status.isSuccess()) {
-            // Update cache
-            if (action == Action.ADD) {
-                outboundIpRewriteCache.add(cacheKey);
-            } else {
-                outboundIpRewriteCache.remove(cacheKey);
-            }
-        }
+        this.programOutboundIpRewriteStage2(fid, action);
     }
 
     private Status programOutboundIpRewriteStage2(FloatIpData fid, Action action) {
@@ -1167,12 +1306,12 @@ public class NeutronL3Adapter implements ConfigInterface {
 
         if (status.isSuccess()) {
             final boolean isSkipped = outboundNatProvider == null;
-            LOGGER.debug("programOutboundIpRewriteStage2 {} for dpid {} seg {} fixedIpAddress {} floatIp {}" +
+            LOG.debug("programOutboundIpRewriteStage2 {} for dpid {} seg {} fixedIpAddress {} floatIp {}" +
                             " action {}",
                          isSkipped ? "skipped" : "programmed",
                          fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
         } else {
-            LOGGER.error("programOutboundIpRewriteStage2 failed for dpid {} seg {} fixedIpAddress {} floatIp {}" +
+            LOG.error("programOutboundIpRewriteStage2 failed for dpid {} seg {} fixedIpAddress {} floatIp {}" +
                          " action {} status:{}",
                          fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action, status);
         }
@@ -1205,6 +1344,111 @@ public class NeutronL3Adapter implements ConfigInterface {
         return null;
     }
 
+    private Long getDpidForExternalBridge(Node node) {
+        // Check if node is integration bridge; and only then return its dpid
+        if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+            return southbound.getDataPathId(node);
+        }
+        return null;
+    }
+
+    private Node getExternalBridgeNode(){
+        //Pickup the first node that has external bridge (br-ex).
+        //NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
+        //the external network is reachable from every br-ex
+        // TODO: Consider other deployment scenario, and thing of better solution.
+        List<Node> allBridges = nodeCacheManager.getBridgeNodes();
+        for(Node node : allBridges){
+            if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
+        if (gatewayPort.getFixedIPs() == null) {
+            return null;
+        }
+        for (Neutron_IPs neutronIPs : gatewayPort.getFixedIPs()) {
+            String subnetUUID = neutronIPs.getSubnetUUID();
+            NeutronSubnet extSubnet = neutronSubnetCache.getSubnet(subnetUUID);
+            if (extSubnet != null && extSubnet.getGatewayIP() != null) {
+                return extSubnet;
+            }
+            if (extSubnet == null) {
+                // TODO: when subnet is created, try again.
+                LOG.debug("subnet {} in not found", subnetUUID);
+             }
+        }
+        return null;
+    }
+
+     private void cleanupRouterCache(final NeutronRouter_Interface neutronRouterInterface) {
+         /*
+          *  Fix for 4277
+          *  Remove the router cache only after deleting the neutron
+          *  port l3 flows.
+          */
+         final NeutronPort neutronPort = neutronPortCache.getPort(neutronRouterInterface.getPortUUID());
+
+         if (neutronPort != null) {
+             networkIdToRouterMacCache.remove(neutronPort.getNetworkUUID());
+             networkIdToRouterIpListCache.remove(neutronPort.getNetworkUUID());
+             subnetIdToRouterInterfaceCache.remove(neutronRouterInterface.getSubnetUUID());
+         }
+     }
+
+    public void triggerGatewayMacResolver(final Node node, final NeutronPort gatewayPort ){
+
+        Preconditions.checkNotNull(node);
+        Preconditions.checkNotNull(gatewayPort);
+        NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
+
+        if(externalNetwork != null){
+            if(externalNetwork.isRouterExternal()){
+                final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
+
+                // TODO: address IPv6 case.
+                if (externalSubnet != null &&
+                    externalSubnet.getIpVersion() == 4 &&
+                    gatewayPort.getFixedIPs() != null) {
+                    LOG.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId());
+                    ListenableFuture<MacAddress> gatewayMacAddress =
+                        gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
+                                                             new Ipv4Address(externalSubnet.getGatewayIP()),
+                                                             new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
+                                                             new MacAddress(gatewayPort.getMacAddress()),
+                                                             true);
+                    if(gatewayMacAddress != null){
+                        Futures.addCallback(gatewayMacAddress, new FutureCallback<MacAddress>(){
+                            @Override
+                            public void onSuccess(MacAddress result) {
+                                if(result != null){
+                                    if(!result.getValue().equals(externalRouterMac)){
+                                        updateExternalRouterMac(result.getValue());
+                                        LOG.info("Resolved MAC address for gateway IP {} is {}", externalSubnet.getGatewayIP(),result.getValue());
+                                    }
+                                }else{
+                                    LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
+                                }
+                            }
+
+                            @Override
+                            public void onFailure(Throwable t) {
+                                LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
+                            }
+                        }, gatewayMacResolverPool);
+                    }
+                } else {
+                    LOG.warn("No gateway IP address found for external network {}", externalNetwork);
+                }
+            }
+        }else{
+            LOG.warn("Neutron network not found for router interface {}", gatewayPort);
+        }
+    }
+
     /**
      * Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
      *
@@ -1216,7 +1460,7 @@ public class NeutronL3Adapter implements ConfigInterface {
     }
 
     @Override
-    public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
+    public void setDependencies(ServiceReference serviceReference) {
         tenantNetworkManager =
                 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
         configurationService =
@@ -1235,7 +1479,10 @@ public class NeutronL3Adapter implements ConfigInterface {
                 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
         southbound =
                 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
-
+        gatewayMacResolver =
+                (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
+        securityServicesManager =
+                (SecurityServicesManager) ServiceHelper.getGlobalInstance(SecurityServicesManager.class, this);
         initL3AdapterMembers();
     }
 
@@ -1247,6 +1494,8 @@ public class NeutronL3Adapter implements ConfigInterface {
             neutronPortCache = (INeutronPortCRUD)impl;
         } else if (impl instanceof INeutronSubnetCRUD) {
             neutronSubnetCache = (INeutronSubnetCRUD)impl;
+        } else if (impl instanceof INeutronFloatingIPCRUD) {
+            neutronFloatingIpCache = (INeutronFloatingIPCRUD)impl;
         } else if (impl instanceof ArpProvider) {
             arpProvider = (ArpProvider)impl;
         } else if (impl instanceof InboundNatProvider) {
@@ -1257,6 +1506,9 @@ public class NeutronL3Adapter implements ConfigInterface {
             routingProvider = (RoutingProvider)impl;
         } else if (impl instanceof L3ForwardingProvider) {
             l3ForwardingProvider = (L3ForwardingProvider)impl;
+        }else if (impl instanceof GatewayMacResolver) {
+            gatewayMacResolver = (GatewayMacResolver)impl;
         }
+        populateL3ForwardingCaches();
     }
 }