Improve log messages.
[netvirt.git] / openstack / net-virt-providers / src / main / java / org / opendaylight / netvirt / openstack / netvirt / providers / openflow13 / OF13Provider.java
index 356be18770cd1bd0f53506b459b23d2fd67ff1ca..f0a4f8a6ca266b886f37a9d4b5565f67e1cd9641 100644 (file)
@@ -8,23 +8,23 @@
 
 package org.opendaylight.netvirt.openstack.netvirt.providers.openflow13;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
 import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
-
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronNetwork;
-import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronPort;
-import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityGroup;
-import org.opendaylight.netvirt.openstack.netvirt.translator.Neutron_IPs;
 import org.opendaylight.netvirt.openstack.netvirt.MdsalHelper;
 import org.opendaylight.netvirt.openstack.netvirt.NetworkHandler;
 import org.opendaylight.netvirt.openstack.netvirt.api.BridgeConfigurationManager;
@@ -33,10 +33,12 @@ import org.opendaylight.netvirt.openstack.netvirt.api.ConfigurationService;
 import org.opendaylight.netvirt.openstack.netvirt.api.Constants;
 import org.opendaylight.netvirt.openstack.netvirt.api.EgressAclProvider;
 import org.opendaylight.netvirt.openstack.netvirt.api.IngressAclProvider;
+import org.opendaylight.netvirt.openstack.netvirt.api.L2ForwardingLearnProvider;
 import org.opendaylight.netvirt.openstack.netvirt.api.L2ForwardingProvider;
 import org.opendaylight.netvirt.openstack.netvirt.api.NetworkingProvider;
 import org.opendaylight.netvirt.openstack.netvirt.api.NetworkingProviderManager;
 import org.opendaylight.netvirt.openstack.netvirt.api.NodeCacheManager;
+import org.opendaylight.netvirt.openstack.netvirt.api.ResubmitAclLearnProvider;
 import org.opendaylight.netvirt.openstack.netvirt.api.SecurityServicesManager;
 import org.opendaylight.netvirt.openstack.netvirt.api.Southbound;
 import org.opendaylight.netvirt.openstack.netvirt.api.Status;
@@ -44,10 +46,14 @@ import org.opendaylight.netvirt.openstack.netvirt.api.StatusCode;
 import org.opendaylight.netvirt.openstack.netvirt.api.TenantNetworkManager;
 import org.opendaylight.netvirt.openstack.netvirt.providers.ConfigInterface;
 import org.opendaylight.netvirt.openstack.netvirt.providers.NetvirtProvidersProvider;
+import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronNetwork;
+import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronPort;
+import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityGroup;
+import org.opendaylight.netvirt.openstack.netvirt.translator.Neutron_IPs;
 import org.opendaylight.netvirt.utils.mdsal.openflow.FlowUtils;
 import org.opendaylight.netvirt.utils.mdsal.openflow.InstructionUtils;
 import org.opendaylight.netvirt.utils.servicehelper.ServiceHelper;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
@@ -95,13 +101,6 @@ import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.util.concurrent.CheckedFuture;
-
-
 /**
  * Open vSwitch OpenFlow 1.3 Networking Provider for OpenStack Neutron
  *
@@ -129,6 +128,8 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
     private volatile IngressAclProvider ingressAclProvider;
     private volatile EgressAclProvider egressAclProvider;
     private volatile NodeCacheManager nodeCacheManager;
+    private volatile ResubmitAclLearnProvider resubmitAclLearnProvider;
+    private volatile L2ForwardingLearnProvider l2ForwardingLearnProvider;
     private volatile L2ForwardingProvider l2ForwardingProvider;
 
     public static final String NAME = "OF13Provider";
@@ -185,14 +186,16 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
     private boolean addTunnelPort (Node node, String tunnelType, InetAddress src, InetAddress dst) {
         String tunnelBridgeName = configurationService.getIntegrationBridgeName();
         String portName = getTunnelName(tunnelType, dst);
-        LOG.info("addTunnelPort enter: portName: {}", portName);
+        LOG.trace("Added TunnelPort : portName: {}", portName);
         if (southbound.extractTerminationPointAugmentation(node, portName) != null
                 || southbound.isTunnelTerminationPointExist(node, tunnelBridgeName, portName)) {
-            LOG.info("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node.getNodeId().getValue());
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Tunnel {} is present in {} of {}", portName, tunnelBridgeName, node.getNodeId().getValue());
+            }
             return true;
         }
 
-        Map<String, String> options = Maps.newHashMap();
+        Map<String, String> options = new HashMap<>();
         options.put("key", "flow");
         options.put("local_ip", src.getHostAddress());
         options.put("remote_ip", dst.getHostAddress());
@@ -202,7 +205,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
             return false;
         }
 
-            LOG.info("addTunnelPort exit: portName: {}", portName);
+        LOG.info("addTunnelPort exit: portName: {}", portName);
         return true;
     }
 
@@ -267,28 +270,14 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         handleTunnelFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, localPort, true);
 
         /*
-         * TODO : Optimize the following 2 writes to be restricted only for the very first port known in a segment.
-         */
-        /*
-         * Table(1) Rule #3
-         * ----------------
-         * Match:  Any remaining Ingress Local VM Packets
-         * Action: Drop w/ a low priority
-         * -------------------------------------------
-         * table=1,priority=8192,tun_id=0x5 actions=goto_table:2
-         */
-
-        handleTunnelMiss(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, true);
-
-        /*
-         * Table(2) Rule #3
+         * Table(110) Rule #1
          * ----------------
-         * Match: Any Remaining Flows w/a TunID
-         * Action: Drop w/ a low priority
-         * table=2,priority=8192,tun_id=0x5 actions=drop
+         * Match: Tunnel ID and unknown unicast
+         * table=110,priority=16380,tun_id=0x5,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 \
+         * actions=output:2,3,4,5
          */
 
-        handleLocalTableMiss(dpid, TABLE_2_LOCAL_FORWARD, segmentationId, true);
+        handleTunnelUnknownUcastFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, localPort, true);
     }
 
     private void removeLocalBridgeRules(Node node, Long dpid, String segmentationId, String attachedMac, long localPort) {
@@ -330,6 +319,16 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
         handleLocalBcastOut(dpid, TABLE_2_LOCAL_FORWARD, segmentationId, localPort, false);
         handleTunnelFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, localPort, false);
+
+        /*
+         * Table(110) Rule #1
+         * ----------------
+         * Match: Tunnel ID and unknown unicast
+         * table=110,priority=16380,tun_id=0x5,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 \
+         * actions=output:2,3,4,5
+         */
+
+        handleTunnelUnknownUcastFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, localPort, false);
     }
 
     private void programLocalIngressTunnelBridgeRules(Node node, Long dpid, String segmentationId, String attachedMac, long tunnelOFPort, long localPort) {
@@ -354,6 +353,15 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
         handleTunnelFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, tunnelOFPort, true);
 
+        /*
+         * Table(110) Rule #1
+         * ----------------
+         * Match: Tunnel ID and unknown unicast
+         * table=110,priority=16380,tun_id=0x5,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 \
+         * actions=output:2,3,4,5
+         */
+        handleTunnelUnknownUcastFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, tunnelOFPort, true);
+
     }
 
     private void programRemoteEgressTunnelBridgeRules(Node node, Long dpid, String segmentationId, String attachedMac, long tunnelOFPort, long localPort) {
@@ -386,30 +394,6 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
     /* Remove tunnel rules if last node in this tenant network */
     private void removePerTunnelRules(Node node, Long dpid, String segmentationId, long tunnelOFPort) {
-        /*
-         * TODO : Optimize the following 2 writes to be restricted only for the very first port known in a segment.
-         */
-        /*
-         * Table(1) Rule #3
-         * ----------------
-         * Match:  Any remaining Ingress Local VM Packets
-         * Action: Drop w/ a low priority
-         * -------------------------------------------
-         * table=1,priority=8192,tun_id=0x5 actions=goto_table:2
-         */
-
-        handleTunnelMiss(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, false);
-
-        /*
-         * Table(2) Rule #3
-         * ----------------
-         * Match: Any Remaining Flows w/a TunID
-         * Action: Drop w/ a low priority
-         * table=2,priority=8192,tun_id=0x5 actions=drop
-         */
-
-        handleLocalTableMiss(dpid, TABLE_2_LOCAL_FORWARD, segmentationId, false);
-
         /*
          * Table(0) Rule #2
          * ----------------
@@ -430,6 +414,16 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
          */
 
         handleTunnelFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, tunnelOFPort, false);
+
+        /*
+         * Table(110) Rule #1
+         * ----------------
+         * Match: Tunnel ID and unknown unicast
+         * table=110,priority=16380,tun_id=0x5,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 \
+         * actions=output:2,3,4,5
+         */
+
+        handleTunnelUnknownUcastFloodOut(dpid, TABLE_1_ISOLATE_TENANT, TABLE_2_LOCAL_FORWARD, segmentationId, tunnelOFPort, false);
     }
 
     private void programLocalVlanRules(Node node, Long dpid, String segmentationId, String attachedMac, long localPort) {
@@ -1009,6 +1003,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
             LOG.info("Port security is not enabled" + intf);
             return;
         }
+        org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId nodeId = node.getNodeId();
         NeutronPort dhcpPort = securityServicesManager.getDhcpServerPort(intf);
         List<Neutron_IPs> srcAddressList = null;
         if (null != dhcpPort) {
@@ -1032,9 +1027,9 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
                                                                            Constants.EXTERNAL_ID_INTERFACE_ID);
             for (NeutronSecurityGroup securityGroupInPort:securityGroupListInPort) {
                 ingressAclProvider.programPortSecurityGroup(dpid, segmentationId, attachedMac, localPort,
-                                                            securityGroupInPort, neutronPortId, write);
+                                                            securityGroupInPort, neutronPortId, nodeId, write);
                 egressAclProvider.programPortSecurityGroup(dpid, segmentationId, attachedMac, localPort,
-                                                           securityGroupInPort, neutronPortId, write);
+                                                           securityGroupInPort, neutronPortId, nodeId, write);
             }
 
         } else {
@@ -1155,6 +1150,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
             boolean sourceTunnelStatus = false;
             boolean destTunnelStatus = false;
+            boolean isSrcinNw = tenantNetworkManager.isTenantNetworkPresentInNode(srcBridgeNode, segmentationId);
             for (Node dstNode : nodes.values()) {
                 InetAddress src = configurationService.getTunnelEndPoint(srcNode);
                 InetAddress dst = configurationService.getTunnelEndPoint(dstNode);
@@ -1164,12 +1160,26 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
                     Node dstBridgeNode = southbound.getBridgeNode(dstNode,
                             configurationService.getIntegrationBridgeName());
 
-                    if (dstBridgeNode != null){
+                    if (dstBridgeNode != null) {
                         destTunnelStatus = addTunnelPort(dstBridgeNode, networkType, dst, src);
                     }
-
+                    if (sourceTunnelStatus &&  destTunnelStatus) {
+                        LOG.debug("Created Source and destination TunnelPorts :{}, {}", src, dst);
+                    }  else {
+                        LOG.debug("Source and destination TunnelPort status :{}, {}", sourceTunnelStatus, destTunnelStatus);
+                    }
                     if (sourceTunnelStatus) {
-                        programTunnelRules(networkType, segmentationId, dst, srcBridgeNode, intf, true);
+                        boolean isDestinNw = tenantNetworkManager.isTenantNetworkPresentInNode(dstBridgeNode, segmentationId);
+                        //Check whether the network is present in src & dst node
+                        //If only present , add vxlan ports in TunnelRules for both nodes (bug# 5614)
+                        if (isSrcinNw && isDestinNw) {
+                            programTunnelRules(networkType, segmentationId, dst, srcBridgeNode, intf, true);
+                            programTunnelRules(networkType, segmentationId, src, dstBridgeNode, intf, true);
+                        } else if (configurationService.isRemoteMacLearnEnabled()) {
+                            //Even if to learn remote MAC, a network doesn't exist in a node,
+                            //a vxlan port is added in TunnelRules for both nodes.(bug# 6474)
+                            programTunnelRules(networkType, segmentationId, dst, srcBridgeNode, intf, true);
+                        }
                     }
                     if (destTunnelStatus) {
                         programTunnelRules(networkType, segmentationId, src, dstBridgeNode, intf, false);
@@ -1197,7 +1207,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
     }
 
     private void triggerInterfaceUpdates(Node node) {
-        LOG.debug("enter triggerInterfaceUpdates for {}", node.getNodeId());
+        LOG.debug("enter triggerInterfaceUpdates for {}", node.getNodeId());
         List<OvsdbTerminationPointAugmentation> ports = southbound.extractTerminationPointAugmentations(node);
         if (ports != null && !ports.isEmpty()) {
             for (OvsdbTerminationPointAugmentation port : ports) {
@@ -1221,6 +1231,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         nodes.remove(southbound.extractBridgeOvsdbNodeId(srcNode));
 
         LOG.info("Delete intf " + intf.getName() + " isLastInstanceOnNode " + isLastInstanceOnNode);
+        String segmentationId = network.getProviderSegmentationID();
         List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(srcNode);
         if (southbound.isTunnel(intf)) {
             // Delete tunnel port
@@ -1239,27 +1250,38 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
             deletePhysicalPort(srcNode, intf.getName());
         } else {
             // delete all other interfaces
-            removeLocalRules(network.getProviderNetworkType(), network.getProviderSegmentationID(),
+            removeLocalRules(network.getProviderNetworkType(), segmentationId,
                     srcNode, intf);
 
             if (isVlan(network.getProviderNetworkType())) {
                 removeVlanRules(network, srcNode, intf, isLastInstanceOnNode);
             } else if (isTunnel(network.getProviderNetworkType())) {
 
+                Node srcBridgeNode = southbound.getBridgeNode(srcNode, configurationService.getIntegrationBridgeName());
                 for (Node dstNode : nodes.values()) {
                     InetAddress src = configurationService.getTunnelEndPoint(srcNode);
                     InetAddress dst = configurationService.getTunnelEndPoint(dstNode);
                     if ((src != null) && (dst != null)) {
                         LOG.info("Remove tunnel rules for interface "
                                 + intf.getName() + " on srcNode " + srcNode.getNodeId().getValue());
-                        removeTunnelRules(tunnelType, network.getProviderSegmentationID(),
+                        removeTunnelRules(tunnelType, segmentationId,
                                 dst, srcNode, intf, true, isLastInstanceOnNode);
                         Node dstBridgeNode = southbound.getBridgeNode(dstNode, Constants.INTEGRATION_BRIDGE);
-                        if (dstBridgeNode != null){
+                        //While removing last instance , check whether the network present in src node
+                        //If network is not present in src node AND REMOTE MAC LEARNING IS not enabled,
+                        //remove the vxlan port of src from dst node in TunnelRules(Bug# 5614)
+                        boolean isSrcinNw = tenantNetworkManager.isTenantNetworkPresentInNode(srcBridgeNode, segmentationId);
+                        if (dstBridgeNode != null) {
+                            //To learn remote MAC, also to make the flooding the node by which a network
+                            //doesn't exist in a node, TunnelRules leaves it.
+                            if (!isSrcinNw && !configurationService.isRemoteMacLearnEnabled()) {
+                                removeTunnelRules(tunnelType, segmentationId,
+                                    src, dstBridgeNode, intf, true, isLastInstanceOnNode);
+                            }
                             LOG.info("Remove tunnel rules for interface "
                                     + intf.getName() + " on dstNode " + dstNode.getNodeId().getValue());
-                            removeTunnelRules(tunnelType, network.getProviderSegmentationID(),
-                                    src, dstBridgeNode, intf, false, isLastInstanceOnNode);
+                            removeTunnelRules(tunnelType, segmentationId, src,
+                                    dstBridgeNode, intf, false, isLastInstanceOnNode);
                         }
                     } else {
                         LOG.warn("Tunnel end-point configuration missing. Please configure it in "
@@ -1283,9 +1305,13 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
     private void initializeFlowRules(Node node, String bridgeName) {
         Long dpid = southbound.getDataPathId(node);
+        if (bridgeName.equals(configurationService.getIntegrationBridgeName())) {
+            resubmitAclLearnProvider.programResubmit(dpid);
+        }
         String datapathId = southbound.getDatapathId(node);
-        LOG.info("initializeFlowRules: bridgeName: {}, dpid: {} - {}",
-                bridgeName, dpid, datapathId);
+        LOG.trace("initializeFlowRules: bridgeName: {}, datapathId: {} ",
+                bridgeName, datapathId);
+        String brExt = null;
 
         if (dpid == 0L) {
             LOG.debug("Openflow Datapath-ID not set for the integration bridge in {}", node);
@@ -1301,12 +1327,25 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
         writeLLDPRule(dpid);
 
+        /*
+         * Table(105) Rule #1
+         * -------------------
+         * Match: reg0=0x2
+         * Action: learn and goto next table
+         */
+
+        writeL2ForwardingLearnRule(dpid);
+
         if (bridgeName.equals(configurationService.getIntegrationBridgeName()) &&
                 NetvirtProvidersProvider.getTableOffset() != 0) {
             classifierProvider.programGotoTable(dpid,true);
         }
 
-        if (bridgeName.equals(configurationService.getExternalBridgeName())) {
+        if (configurationService.isL3MultipleExternalNetworkEnabled()) {
+            brExt = bridgeConfigurationManager.getMultipleExternalBridge(node);
+        }
+        if (bridgeName.equals(configurationService.getExternalBridgeName()) ||
+                (bridgeName.equals(brExt))) {
             writeNormalRule(dpid);
         }
     }
@@ -1323,6 +1362,18 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         classifierProvider.programLLDPPuntRule(dpidLong);
     }
 
+    /*
+     * Create an L2Forwarding mac learn Flow Rule
+     * Match: reg0 = ClassifierService.REG_VALUE_FROM_REMOTE
+     * Action: learn and goto next table
+     */
+
+    private void writeL2ForwardingLearnRule(Long dpidLong) {
+        if (configurationService.isRemoteMacLearnEnabled()) {
+            l2ForwardingLearnProvider.programL2ForwardingLearnRule(dpidLong);
+        }
+    }
+
     /*
      * Create a NORMAL Table Miss Flow Rule
      * Match: any
@@ -1342,7 +1393,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         InstructionsBuilder isb = new InstructionsBuilder();
 
         // Instructions List Stores Individual Instructions
-        List<Instruction> instructions = Lists.newArrayList();
+        List<Instruction> instructions = new ArrayList<>();
 
         // Call the InstructionBuilder Methods Containing Actions
         InstructionUtils.createNormalInstructions(FlowUtils.getNodeName(dpidLong), ib);
@@ -1467,6 +1518,22 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         l2ForwardingProvider.programTunnelFloodOut(dpidLong, segmentationId, OFPortOut, write);
     }
 
+    /*
+     * (Table:110) Flooding local unknown unicast Traffic
+     * Match: TunnelID and Unknown unicast and Local InPort
+     * Instruction: Set TunnelID and GOTO Table Tunnel Table (n)
+     * table=110,priority=16380,tun_id=0x5,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 \
+     * actions=output:10,output:11
+     */
+
+    private void handleTunnelUnknownUcastFloodOut(Long dpidLong, Short writeTable,
+            Short localTable, String segmentationId,
+            Long OFPortOut, boolean write) {
+        if (configurationService.isRemoteMacLearnEnabled()) {
+            l2ForwardingProvider.programTunnelUnknownUcastFloodOut(dpidLong, segmentationId, OFPortOut, write);
+        }
+    }
+
     /*
      * (Table:1) Egress VLAN Traffic
      * Match: Destination Ethernet Addr and VLAN id
@@ -1482,20 +1549,6 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         //l2ForwardingProvider.programVlanFloodOut(dpidLong, segmentationId, localPort, ethPort, write);
     }
 
-    /*
-     * (Table:1) Table Drain w/ Catch All
-     * Match: Tunnel ID
-     * Action: GOTO Local Table (10)
-     * table=2,priority=8192,tun_id=0x5 actions=drop
-     */
-
-    private void handleTunnelMiss(Long dpidLong, Short writeTable,
-            Short goToTableId, String segmentationId,
-            boolean write) {
-        l2ForwardingProvider.programTunnelMiss(dpidLong, segmentationId, write);
-    }
-
-
     /*
      * (Table:1) Table Drain w/ Catch All
      * Match: Vlan ID
@@ -1562,18 +1615,6 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         l2ForwardingProvider.programLocalVlanBcastOut(dpidLong, segmentationId, localPort, ethPort, write);
     }
 
-    /*
-     * (Table:1) Local Table Miss
-     * Match: Any Remaining Flows w/a TunID
-     * Action: Drop w/ a low priority
-     * table=2,priority=8192,tun_id=0x5 actions=drop
-     */
-
-    private void handleLocalTableMiss(Long dpidLong, Short writeTable,
-            String segmentationId, boolean write) {
-        l2ForwardingProvider.programLocalTableMiss(dpidLong, segmentationId, write);
-    }
-
     /*
      * (Table:1) Local Table Miss
      * Match: Any Remaining Flows w/a VLAN ID
@@ -1668,9 +1709,11 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
     /**
      * Create Output Port Group Instruction
      *
+     * @param nodeBuilder Node Builder
      * @param ib       Map InstructionBuilder without any instructions
      * @param dpidLong Long the datapath ID of a switch/node
      * @param port     Long representing a port on a switch/node
+     * @param instructions List of instructions
      * @return ib InstructionBuilder Map with instructions
      */
     // TODO This method is referenced from commented code in L2ForwardingService (which needs to be checked)
@@ -1682,7 +1725,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         NodeConnectorId ncid = new NodeConnectorId(Constants.OPENFLOW_NODE_PREFIX + dpidLong + ":" + port);
         LOG.debug("createOutputGroupInstructions() Node Connector ID is - Type=openflow: DPID={} port={} existingInstructions={}", dpidLong, port, instructions);
 
-        List<Action> actionList = Lists.newArrayList();
+        List<Action> actionList = new ArrayList<>();
         ActionBuilder ab = new ActionBuilder();
 
         List<Action> existingActions;
@@ -1748,7 +1791,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
             if (addNew && !buckets.getBucket().isEmpty()) {
                 /* the new output action is not in the bucket, add to bucket */
                 Bucket bucket = buckets.getBucket().get(0);
-                List<Action> bucketActionList = Lists.newArrayList();
+                List<Action> bucketActionList = new ArrayList<>();
                 bucketActionList.addAll(bucket.getAction());
                 /* set order for new action and add to action list */
                 ab.setOrder(bucketActionList.size());
@@ -1757,7 +1800,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
 
                 /* set bucket and buckets list. Reset groupBuilder with new buckets.*/
                 BucketsBuilder bucketsBuilder = new BucketsBuilder();
-                List<Bucket> bucketList = Lists.newArrayList();
+                List<Bucket> bucketList = new ArrayList<>();
                 BucketBuilder bucketBuilder = new BucketBuilder();
                 bucketBuilder.setBucketId(new BucketId((long) 1));
                 bucketBuilder.setKey(new BucketKey(new BucketId((long) 1)));
@@ -1777,13 +1820,13 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
             groupBuilder.setBarrier(false);
 
             BucketsBuilder bucketBuilder = new BucketsBuilder();
-            List<Bucket> bucketList = Lists.newArrayList();
+            List<Bucket> bucketList = new ArrayList<>();
             BucketBuilder bucket = new BucketBuilder();
             bucket.setBucketId(new BucketId((long) 1));
             bucket.setKey(new BucketKey(new BucketId((long) 1)));
 
             /* put output action to the bucket */
-            List<Action> bucketActionList = Lists.newArrayList();
+            List<Action> bucketActionList = new ArrayList<>();
             /* set order for new action and add to action list */
             ab.setOrder(bucketActionList.size());
             ab.setKey(new ActionKey(bucketActionList.size()));
@@ -1825,9 +1868,11 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
     /**
      * Remove Output Port from action list in group bucket
      *
+     * @param nodeBuilder Node Builder
      * @param ib       Map InstructionBuilder without any instructions
      * @param dpidLong Long the datapath ID of a switch/node
      * @param port     Long representing a port on a switch/node
+     * @param instructions List of instructions
      * @return ib InstructionBuilder Map with instructions
      */
     // TODO This method is referenced from commented code in L2ForwardingService (which needs to be checked)
@@ -1838,7 +1883,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
         NodeConnectorId ncid = new NodeConnectorId(Constants.OPENFLOW_NODE_PREFIX + dpidLong + ":" + port);
         LOG.debug("removeOutputPortFromGroup() Node Connector ID is - Type=openflow: DPID={} port={} existingInstructions={}", dpidLong, port, instructions);
 
-        List<Action> actionList = Lists.newArrayList();
+        List<Action> actionList = new ArrayList<>();
         ActionBuilder ab;
 
         List<Action> existingActions;
@@ -1877,7 +1922,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
             /* modify the action bucket in group */
             groupBuilder = new GroupBuilder(group);
             Buckets buckets = groupBuilder.getBuckets();
-            List<Action> bucketActions = Lists.newArrayList();
+            List<Action> bucketActions = new ArrayList<>();
             for (Bucket bucket : buckets.getBucket()) {
                 int index = 0;
                 boolean isPortDeleted = false;
@@ -1919,7 +1964,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
                 /* rewrite the group to group table */
                 /* set bucket and buckets list. Reset groupBuilder with new buckets.*/
                 BucketsBuilder bucketsBuilder = new BucketsBuilder();
-                List<Bucket> bucketList = Lists.newArrayList();
+                List<Bucket> bucketList = new ArrayList<>();
                 BucketBuilder bucketBuilder = new BucketBuilder();
                 bucketBuilder.setBucketId(new BucketId((long) 1));
                 bucketBuilder.setKey(new BucketKey(new BucketId((long) 1)));
@@ -1949,14 +1994,14 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
     public void initializeOFFlowRules(Node openflowNode) {
         String bridgeName = southbound.getBridgeName(openflowNode);
         LOG.info("initializeOFFlowRules: bridgeName: {}", bridgeName);
-        if (bridgeName.equals(configurationService.getIntegrationBridgeName())) {
-            initializeFlowRules(openflowNode, configurationService.getIntegrationBridgeName());
-            triggerInterfaceUpdates(openflowNode);
-        } else if (bridgeName.equals(configurationService.getExternalBridgeName())) {
-            initializeFlowRules(openflowNode, configurationService.getExternalBridgeName());
-            LOG.info("initializeOFFlowRules after writeFlow: bridgeName: {}", bridgeName);
+        String brExt = null;
+        if (configurationService.isL3MultipleExternalNetworkEnabled()) {
+            brExt = bridgeConfigurationManager.getMultipleExternalBridge(openflowNode);
+        }
+        if (bridgeName.equals(configurationService.getIntegrationBridgeName()) ||
+                bridgeName.equals(configurationService.getExternalBridgeName()) || bridgeName.equals(brExt)) {
+            initializeFlowRules(openflowNode, bridgeName);
             triggerInterfaceUpdates(openflowNode);
-            LOG.info("initializeOFFlowRules after triggerUpdates: bridgeName: {}", bridgeName);
         }
     }
 
@@ -1978,12 +2023,16 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider {
                 (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
         nodeCacheManager =
                 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
+        resubmitAclLearnProvider =
+                (ResubmitAclLearnProvider) ServiceHelper.getGlobalInstance(ResubmitAclLearnProvider.class, this);
         classifierProvider =
                 (ClassifierProvider) ServiceHelper.getGlobalInstance(ClassifierProvider.class, this);
         ingressAclProvider =
                 (IngressAclProvider) ServiceHelper.getGlobalInstance(IngressAclProvider.class, this);
         egressAclProvider =
                 (EgressAclProvider) ServiceHelper.getGlobalInstance(EgressAclProvider.class, this);
+        l2ForwardingLearnProvider =
+                (L2ForwardingLearnProvider) ServiceHelper.getGlobalInstance(L2ForwardingLearnProvider.class, this);
         l2ForwardingProvider =
                 (L2ForwardingProvider) ServiceHelper.getGlobalInstance(L2ForwardingProvider.class, this);
         securityServicesManager =