ACL: Support for non-conntrack supported traffic.
[netvirt.git] / vpnservice / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / AbstractAclServiceImpl.java
index 3758c31b06ceaa00ab36e574d5b7841299b378cd..8b67508feb744ef13ea2e1b7e8419b215d977eea 100644 (file)
@@ -7,38 +7,58 @@
  */
 package org.opendaylight.netvirt.aclservice;
 
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.collect.Lists;
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-
+import java.util.SortedSet;
+import java.util.stream.Collectors;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.InstructionInfo;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
 import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxConntrack;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxConntrack.NxCtAction;
 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
+import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
+import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchCtState;
+import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
 import org.opendaylight.netvirt.aclservice.api.AclServiceListener;
 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
-import org.opendaylight.netvirt.aclservice.api.utils.AclInterfaceCacheUtil;
+import org.opendaylight.netvirt.aclservice.utils.AclConntrackClassifierType;
+import org.opendaylight.netvirt.aclservice.utils.AclConstants;
 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
+import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
+import org.opendaylight.netvirt.aclservice.utils.StatefulAclServiceHelper;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
 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.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
 import org.slf4j.Logger;
@@ -50,31 +70,42 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
 
     protected final IMdsalApiManager mdsalManager;
     protected final DataBroker dataBroker;
+    protected final ManagedNewTransactionRunner txRunner;
     protected final Class<? extends ServiceModeBase> serviceMode;
     protected final AclDataUtil aclDataUtil;
     protected final AclServiceUtils aclServiceUtils;
+    protected final JobCoordinator jobCoordinator;
+    protected final AclInterfaceCache aclInterfaceCache;
+
+    protected final Class<? extends DirectionBase> direction;
+    protected final String directionString;
 
     /**
      * Initialize the member variables.
      *
-     * @param serviceMode
-     *            the service mode
-     * @param dataBroker
-     *            the data broker instance.
-     * @param mdsalManager
-     *            the mdsal manager instance.
-     * @param aclDataUtil
-     *            the acl data util.
-     * @param aclServiceUtils
-     *            the acl service util.
+     * @param serviceMode the service mode
+     * @param dataBroker the data broker instance.
+     * @param mdsalManager the mdsal manager instance.
+     * @param aclDataUtil the acl data util.
+     * @param aclServiceUtils the acl service util.
+     * @param jobCoordinator the job coordinator
+     * @param aclInterfaceCache the acl interface cache
      */
     public AbstractAclServiceImpl(Class<? extends ServiceModeBase> serviceMode, DataBroker dataBroker,
-            IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil, AclServiceUtils aclServiceUtils) {
+            IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil, AclServiceUtils aclServiceUtils,
+            JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
         this.dataBroker = dataBroker;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
         this.mdsalManager = mdsalManager;
         this.serviceMode = serviceMode;
         this.aclDataUtil = aclDataUtil;
         this.aclServiceUtils = aclServiceUtils;
+        this.jobCoordinator = jobCoordinator;
+        this.aclInterfaceCache = aclInterfaceCache;
+
+        this.direction =
+                this.serviceMode.equals(ServiceModeEgress.class) ? DirectionIngress.class : DirectionEgress.class;
+        this.directionString = this.direction.equals(DirectionEgress.class) ? "Egress" : "Ingress";
     }
 
     @Override
@@ -84,15 +115,16 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
             return false;
         }
         if (port.getSecurityGroups() == null) {
-            LOG.error("port security groups cannot be null");
+            LOG.info("Port {} without SGs", port.getInterfaceId());
             return false;
         }
         BigInteger dpId = port.getDpId();
         if (dpId == null || port.getLPortTag() == null) {
-            LOG.error("Unable to find DId from ACL interface with id {}", port.getInterfaceId());
+            LOG.error("Unable to find DpId from ACL interface with id {}", port.getInterfaceId());
             return false;
         }
-        programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), Action.ADD, NwConstants.ADD_FLOW);
+        LOG.debug("Applying ACL on port {} with DpId {}", port, dpId);
+        programAcl(port, Action.ADD, NwConstants.ADD_FLOW);
         updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
         return true;
     }
@@ -100,41 +132,53 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
     @Override
     public boolean bindAcl(AclInterface port) {
         if (port == null || port.getSecurityGroups() == null) {
-            LOG.error("port and port security groups cannot be null");
+            LOG.error("Port and port security groups cannot be null for binding ACL service, port={}", port);
             return false;
         }
         bindService(port);
-        updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
+        if (port.getDpId() != null) {
+            updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
+        }
         return true;
     }
 
     @Override
     public boolean unbindAcl(AclInterface port) {
-        BigInteger dpId = port.getDpId();
-        if (dpId == null) {
-            LOG.error("Unable to find DP Id from ACL interface with id {}", port.getInterfaceId());
+        if (port == null) {
+            LOG.error("Port cannot be null for unbinding ACL service");
             return false;
         }
-        unbindService(port);
-        updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
+        if (port.getDpId() != null) {
+            unbindService(port);
+            updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
+        }
         return true;
     }
 
     @Override
     public boolean updateAcl(AclInterface portBefore, AclInterface portAfter) {
+        // this check is to avoid situations of port update coming before interface state is up
+        if (portAfter.getDpId() == null || portAfter.getLPortTag() == null) {
+            LOG.debug("Unable to find DpId from ACL interface with id {} and lport {}", portAfter.getInterfaceId(),
+                    portAfter.getLPortTag());
+            return false;
+        }
         boolean result = true;
-        boolean isPortSecurityEnable = portAfter.getPortSecurityEnabled();
-        boolean isPortSecurityEnableBefore = portBefore.getPortSecurityEnabled();
+        boolean isPortSecurityEnable = portAfter.isPortSecurityEnabled();
+        boolean isPortSecurityEnableBefore = portBefore.isPortSecurityEnabled();
         // if port security is changed, apply/remove Acls
         if (isPortSecurityEnableBefore != isPortSecurityEnable) {
+            LOG.debug("On ACL update, Port security is {} for {}", isPortSecurityEnable ? "Enabled" :
+                    "Disabled", portAfter.getInterfaceId());
             if (isPortSecurityEnable) {
-                result = applyAcl(portAfter) && bindAcl(portAfter);
+                result = applyAcl(portAfter);
             } else {
-                result = removeAcl(portBefore) && unbindAcl(portBefore);
+                result = removeAcl(portBefore);
             }
         } else if (isPortSecurityEnable) {
             // Acls has been updated, find added/removed Acls and act accordingly.
             processInterfaceUpdate(portBefore, portAfter);
+            LOG.debug("On ACL update, ACL has been updated for {}", portAfter.getInterfaceId());
         }
 
         return result;
@@ -142,91 +186,271 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
 
     private void processInterfaceUpdate(AclInterface portBefore, AclInterface portAfter) {
         BigInteger dpId = portAfter.getDpId();
-        List<AllowedAddressPairs> addedAllowedAddressPairs =
-                AclServiceUtils.getUpdatedAllowedAddressPairs(portAfter.getAllowedAddressPairs(),
-                        portBefore.getAllowedAddressPairs());
-        List<AllowedAddressPairs> deletedAllowedAddressPairs =
-                AclServiceUtils.getUpdatedAllowedAddressPairs(portBefore.getAllowedAddressPairs(),
-                        portAfter.getAllowedAddressPairs());
-        if (deletedAllowedAddressPairs != null && !deletedAllowedAddressPairs.isEmpty()) {
-            programAclWithAllowedAddress(portAfter, deletedAllowedAddressPairs, Action.UPDATE, NwConstants.DEL_FLOW);
+        List<AllowedAddressPairs> addedAaps = AclServiceUtils
+                .getUpdatedAllowedAddressPairs(portAfter.getAllowedAddressPairs(), portBefore.getAllowedAddressPairs());
+        List<AllowedAddressPairs> deletedAaps = AclServiceUtils
+                .getUpdatedAllowedAddressPairs(portBefore.getAllowedAddressPairs(), portAfter.getAllowedAddressPairs());
+        if (deletedAaps != null && !deletedAaps.isEmpty()) {
+            programAclWithAllowedAddress(portBefore, deletedAaps, Action.UPDATE, NwConstants.DEL_FLOW);
+            updateRemoteAclFilterTable(portBefore, portBefore.getSecurityGroups(), deletedAaps, NwConstants.DEL_FLOW);
         }
-        if (addedAllowedAddressPairs != null && !addedAllowedAddressPairs.isEmpty()) {
-            programAclWithAllowedAddress(portAfter, addedAllowedAddressPairs, Action.UPDATE, NwConstants.ADD_FLOW);
+        if (addedAaps != null && !addedAaps.isEmpty()) {
+            programAclWithAllowedAddress(portAfter, addedAaps, Action.UPDATE, NwConstants.ADD_FLOW);
+            updateRemoteAclFilterTable(portAfter, portAfter.getSecurityGroups(), addedAaps, NwConstants.ADD_FLOW);
+        }
+        updateArpForAllowedAddressPairs(dpId, portAfter.getLPortTag(), deletedAaps, portAfter.getAllowedAddressPairs());
+        if (portAfter.getSubnetIpPrefixes() != null && portBefore.getSubnetIpPrefixes() == null) {
+            programBroadcastRules(portAfter, NwConstants.ADD_FLOW);
         }
-        updateArpForAllowedAddressPairs(dpId, portAfter.getLPortTag(), deletedAllowedAddressPairs,
-                portAfter.getAllowedAddressPairs());
-
-        updateAclInterfaceInCache(portBefore);
-        // Have to delete and add all rules because there can be following scenario: Interface1 with SG1, Interface2
-        // with SG2 (which has ACE with remote SG1). Now When we add SG3 to Interface1, the rule for Interface2 which
-        // match the IP of Interface1 will not be installed (but it have to be because Interface1 has more than one SG).
-        // So we need to remove all rules and install them from 0, and we cannot handle only the delta.
-        updateCustomRules(portBefore, portBefore.getSecurityGroups(), NwConstants.DEL_FLOW,
-                portAfter.getAllowedAddressPairs());
-        updateRemoteAclFilterTable(portBefore, NwConstants.DEL_FLOW);
 
-        updateAclInterfaceInCache(portAfter);
+        List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(portAfter.getSecurityGroups(),
+                portBefore.getSecurityGroups());
+        List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(portBefore.getSecurityGroups(),
+                portAfter.getSecurityGroups());
+        if (deletedAcls.isEmpty() && addedAcls.isEmpty()) {
+            LOG.trace("No change w.r.t ACL list for port={}", portAfter.getInterfaceId());
+            return;
+        }
 
-        updateCustomRules(portAfter, portAfter.getSecurityGroups(), NwConstants.ADD_FLOW,
-                portAfter.getAllowedAddressPairs());
-        updateRemoteAclFilterTable(portAfter, NwConstants.ADD_FLOW);
+        handleAclChange(portBefore, deletedAcls, NwConstants.DEL_FLOW);
+        handleAclChange(portAfter, addedAcls, NwConstants.ADD_FLOW);
     }
 
-    private void updateAclInterfaceInCache(AclInterface aclInterfaceNew) {
-        AclInterfaceCacheUtil.addAclInterfaceToCache(aclInterfaceNew.getInterfaceId(), aclInterfaceNew);
-        aclDataUtil.addOrUpdateAclInterfaceMap(aclInterfaceNew.getSecurityGroups(), aclInterfaceNew);
+    private void handleAclChange(AclInterface port, List<Uuid> aclList, int addOrRemove) {
+        int operationForAclRules = (addOrRemove == NwConstants.DEL_FLOW) ? NwConstants.MOD_FLOW : addOrRemove;
+        programAclRules(port, aclList, operationForAclRules);
+        updateRemoteAclFilterTable(port, aclList, port.getAllowedAddressPairs(), addOrRemove);
+        programAclDispatcherTable(port, addOrRemove);
     }
 
-    private void updateCustomRules(AclInterface port, List<Uuid> aclUuidList, int action,
-            List<AllowedAddressPairs> syncAllowedAddresses) {
-        programAclRules(port, aclUuidList, action);
-        syncRemoteAclRules(aclUuidList, action, port.getInterfaceId(), syncAllowedAddresses);
+    protected SortedSet<Integer> getRemoteAclTags(AclInterface port) {
+        return this.direction == DirectionIngress.class ? port.getIngressRemoteAclTags()
+                : port.getEgressRemoteAclTags();
     }
 
-    private void syncRemoteAclRules(List<Uuid> aclUuidList, int action, String currentPortId,
-            List<AllowedAddressPairs> syncAllowedAddresses) {
-        if (aclUuidList == null) {
-            LOG.warn("security groups are null");
+    protected void programAclDispatcherTable(AclInterface port, int addOrRemove) {
+        SortedSet<Integer> remoteAclTags = getRemoteAclTags(port);
+        if (remoteAclTags.isEmpty()) {
+            LOG.debug("No {} rules with remote group id for port={}", this.directionString, port.getInterfaceId());
             return;
         }
+        Integer firstRemoteAclTag = remoteAclTags.first();
+        Integer lastRemoteAclTag = remoteAclTags.last();
+
+        programFirstRemoteAclEntryInDispatcherTable(port, firstRemoteAclTag, addOrRemove);
+        programLastRemoteAclEntryInDispatcherTable(port, lastRemoteAclTag, addOrRemove);
 
-        for (Uuid remoteAclId : aclUuidList) {
-            Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(remoteAclId);
-            if (mapAclWithPortSet == null) {
+        Integer previousRemoteAclTag = firstRemoteAclTag;
+        for (Integer remoteAclTag : remoteAclTags) {
+            if (remoteAclTag.equals(firstRemoteAclTag)) {
                 continue;
             }
-            for (Entry<String, Set<AclInterface>> entry : mapAclWithPortSet.entrySet()) {
-                String aclName = entry.getKey();
-                for (AclInterface port : entry.getValue()) {
-                    if (currentPortId.equals(port.getInterfaceId())
-                            || (port.getSecurityGroups() != null && port.getSecurityGroups().size() == 1)) {
-                        continue;
-                    }
-                    List<Ace> remoteAceList = AclServiceUtils.getAceWithRemoteAclId(dataBroker, port, remoteAclId);
-                    for (Ace ace : remoteAceList) {
-                        programAceRule(port, action, aclName, ace, syncAllowedAddresses);
-                    }
-                }
-            }
+            List<MatchInfoBase> matches = new ArrayList<>();
+            matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(),
+                    previousRemoteAclTag, serviceMode));
+            String flowId = this.directionString + "_ACL_Dispatcher_" + port.getDpId() + "_" + port.getLPortTag() + "_"
+                    + remoteAclTag;
+
+            List<InstructionInfo> instructions =
+                    AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRuleBasedFilterTable());
+            instructions.add(AclServiceUtils.getWriteMetadataForRemoteAclTag(remoteAclTag));
+            syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId,
+                    AclConstants.ACE_GOTO_NEXT_REMOTE_ACL_PRIORITY, "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
+                    instructions, addOrRemove);
+
+            previousRemoteAclTag = remoteAclTag;
         }
     }
 
+    protected void programFirstRemoteAclEntryInDispatcherTable(AclInterface port, Integer firstRemoteAclTag,
+            int addOrRemove) {
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode));
+        String flowId = this.directionString + "_ACL_Dispatcher_First_" + port.getDpId() + "_" + port.getLPortTag()
+                + "_" + firstRemoteAclTag;
+
+        List<InstructionInfo> instructions =
+                AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRuleBasedFilterTable());
+        instructions.add(AclServiceUtils.getWriteMetadataForRemoteAclTag(firstRemoteAclTag));
+        syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, AclConstants.ACE_FIRST_REMOTE_ACL_PRIORITY,
+                "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
+    }
+
+    protected void programLastRemoteAclEntryInDispatcherTable(AclInterface port, Integer lastRemoteAclTag,
+            int addOrRemove) {
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(), lastRemoteAclTag,
+                serviceMode));
+        String flowId = this.directionString + "_ACL_Dispatcher_Last_" + port.getDpId() + "_" + port.getLPortTag() + "_"
+                + lastRemoteAclTag;
+
+        List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
+        syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, AclConstants.ACE_LAST_REMOTE_ACL_PRIORITY,
+                "ACL", 0, 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
+    }
+
+    private void programAcl(AclInterface port, Action action, int addOrRemove) {
+        programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), action, addOrRemove);
+    }
+
     private void programAclWithAllowedAddress(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
             Action action, int addOrRemove) {
         BigInteger dpId = port.getDpId();
         int lportTag = port.getLPortTag();
-        List<Uuid> aclUuidList = port.getSecurityGroups();
+        LOG.debug("Applying ACL Allowed Address on DpId {}, lportTag {}, Action {}", dpId, lportTag, action);
         String portId = port.getInterfaceId();
-        programGeneralFixedRules(dpId, "", allowedAddresses, lportTag, action, addOrRemove);
-        programSpecificFixedRules(dpId, "", allowedAddresses, lportTag, portId, action, addOrRemove);
+        programGeneralFixedRules(port, "", allowedAddresses, action, addOrRemove);
+        programAclPortSpecificFixedRules(dpId, allowedAddresses, lportTag, portId, action, addOrRemove);
         if (action == Action.ADD || action == Action.REMOVE) {
-            programAclRules(port, aclUuidList, addOrRemove);
+            programAclRules(port, port.getSecurityGroups(), addOrRemove);
+            programAclDispatcherTable(port, addOrRemove);
         }
-        syncRemoteAclRules(aclUuidList, addOrRemove, portId, allowedAddresses);
     }
 
 
+    /**
+     * Programs the acl custom rules.
+     *
+     * @param port acl interface
+     * @param aclUuidList the list of acl uuid to be applied
+     * @param addOrRemove whether to delete or add flow
+     * @return program succeeded
+     */
+    protected boolean programAclRules(AclInterface port, List<Uuid> aclUuidList, int addOrRemove) {
+        BigInteger dpId = port.getDpId();
+        LOG.debug("Applying custom rules on DpId {}, lportTag {}", dpId, port.getLPortTag());
+        if (aclUuidList == null || dpId == null) {
+            LOG.warn("{} ACL parameters can not be null. dpId={}, aclUuidList={}", this.directionString, dpId,
+                    aclUuidList);
+            return false;
+        }
+        for (Uuid aclUuid : aclUuidList) {
+            Acl acl = AclServiceUtils.getAcl(dataBroker, aclUuid.getValue());
+            if (null == acl) {
+                LOG.warn("The ACL {} not found in config DS", aclUuid.getValue());
+                continue;
+            }
+            AccessListEntries accessListEntries = acl.getAccessListEntries();
+            List<Ace> aceList = accessListEntries.getAce();
+            for (Ace ace: aceList) {
+                programAceRule(port, ace, addOrRemove);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Programs the ace specific rule.
+     *
+     * @param port acl interface
+     * @param ace rule to be program
+     * @param addOrRemove whether to delete or add flow
+     */
+    protected void programAceRule(AclInterface port, Ace ace, int addOrRemove) {
+        SecurityRuleAttr aceAttr = AclServiceUtils.getAccesssListAttributes(ace);
+        if (!isValidDirection(aceAttr.getDirection())) {
+            LOG.trace("Ignoring {} direction while processing for {} ACE Rule {}", aceAttr.getDirection(),
+                    this.directionString, ace.getRuleName());
+            return;
+        }
+        LOG.debug("Program {} ACE rule for dpId={}, lportTag={}, addOrRemove={}, ace={}, portId={}",
+                this.directionString, port.getDpId(), port.getLPortTag(), addOrRemove, ace.getRuleName(),
+                port.getInterfaceId());
+
+        Matches matches = ace.getMatches();
+        Map<String, List<MatchInfoBase>> flowMap = null;
+        if (matches.getAceType() instanceof AceIp) {
+            flowMap = AclServiceOFFlowBuilder.programIpFlow(matches);
+            if (!AclServiceUtils.doesAceHaveRemoteGroupId(aceAttr)) {
+                // programming for ACE which doesn't have any remote group Id
+                programForAceNotHavingRemoteAclId(port, ace, flowMap, addOrRemove);
+            } else {
+                Uuid remoteAclId = aceAttr.getRemoteGroupId();
+                // programming for ACE which have remote group Id
+                programAceSpecificFlows(port, ace, flowMap, remoteAclId, addOrRemove);
+            }
+        }
+    }
+
+    protected void programForAceNotHavingRemoteAclId(AclInterface port, Ace ace,
+            Map<String, List<MatchInfoBase>> flowMap, int addOrRemove) {
+        if (null == flowMap) {
+            return;
+        }
+
+        MatchInfoBase lportTagMatch = AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode);
+        for (Entry<String, List<MatchInfoBase>> entry : flowMap.entrySet()) {
+            String flowName = entry.getKey();
+            List<MatchInfoBase> matches = entry.getValue();
+            matches.add(lportTagMatch);
+            String flowId = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
+                    + ace.getKey().getRuleName();
+
+            List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
+            int operation = addOrRemove == NwConstants.MOD_FLOW ? NwConstants.DEL_FLOW : addOrRemove;
+            syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId,
+                    AclConstants.ACE_WITHOUT_REMOTE_ACL_PRIORITY, "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
+                    instructions, operation);
+
+            if (addOrRemove != NwConstants.DEL_FLOW) {
+                programAclForExistingTrafficTable(port, ace, addOrRemove, flowName, matches,
+                        AclConstants.ACE_WITHOUT_REMOTE_ACL_PRIORITY);
+            }
+        }
+    }
+
+    protected void programAceSpecificFlows(AclInterface port, Ace ace, Map<String, List<MatchInfoBase>> flowMap,
+            Uuid remoteAclId, int addOrRemove) {
+        if (null == flowMap) {
+            return;
+        }
+        Integer remoteAclTag = this.aclServiceUtils.getAclTag(remoteAclId);
+        if (remoteAclTag == null || remoteAclTag == AclConstants.INVALID_ACL_TAG) {
+            LOG.error("Failed building metadata match for ACL={}. Failed to allocate id", remoteAclId.getValue());
+            return;
+        }
+
+        for (Entry<String, List<MatchInfoBase>> entry : flowMap.entrySet()) {
+            String flowName = entry.getKey();
+            List<MatchInfoBase> matches = entry.getValue();
+            matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(),
+                    remoteAclTag, serviceMode));
+            String flowId = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
+                    + ace.getKey().getRuleName();
+
+            List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRemoteAclTable());
+            int operation = addOrRemove == NwConstants.MOD_FLOW ? NwConstants.DEL_FLOW : addOrRemove;
+            syncFlow(port.getDpId(), getAclRuleBasedFilterTable(), flowId, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0,
+                    0, AclConstants.COOKIE_ACL_BASE, matches, instructions, operation);
+
+            if (addOrRemove != NwConstants.DEL_FLOW) {
+                programAclForExistingTrafficTable(port, ace, addOrRemove, flowName, matches,
+                        AclConstants.ACL_DEFAULT_PRIORITY);
+            }
+        }
+    }
+
+    private void programAclForExistingTrafficTable(AclInterface port, Ace ace, int addOrRemove, String flowName,
+            List<MatchInfoBase> matches, Integer priority) {
+        AceIp acl = (AceIp) ace.getMatches().getAceType();
+        final String newFlowName = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag()
+                + "_" + ((acl.getAceIpVersion() instanceof AceIpv4) ? "_IPv4" : "_IPv6") + "_FlowAfterRuleDeleted";
+
+        final List<MatchInfoBase> newMatches =
+                matches.stream().filter(obj -> !(obj instanceof NxMatchCtState || obj instanceof MatchMetadata))
+                        .collect(Collectors.toList());
+        newMatches.add(AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode));
+        newMatches.add(new NxMatchCtState(AclConstants.TRACKED_RPL_CT_STATE, AclConstants.TRACKED_RPL_CT_STATE_MASK));
+
+        List<InstructionInfo> instructions = StatefulAclServiceHelper
+                .createCtMarkInstructionForNewState(getAclFilterCumDispatcherTable(), port.getElanId());
+        // Reversing the flow add/delete operation for this table.
+        int operation = (addOrRemove == NwConstants.ADD_FLOW) ? NwConstants.DEL_FLOW : NwConstants.ADD_FLOW;
+        syncFlow(port.getDpId(), getAclForExistingTrafficTable(), newFlowName, priority, "ACL", 0,
+                StatefulAclServiceHelper.getHardTimoutForApplyStatefulChangeOnExistingTraffic(ace, aclServiceUtils),
+                AclConstants.COOKIE_ACL_BASE, newMatches, instructions, operation);
+    }
+
     @Override
     public boolean removeAcl(AclInterface port) {
         BigInteger dpId = port.getDpId();
@@ -234,28 +458,32 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
             LOG.error("Unable to find DP Id from ACL interface with id {}", port.getInterfaceId());
             return false;
         }
-        programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), Action.REMOVE, NwConstants.DEL_FLOW);
-        updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW, true);
+        programAcl(port, Action.REMOVE, NwConstants.DEL_FLOW);
+        updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
         return true;
     }
 
     @Override
     public boolean applyAce(AclInterface port, String aclName, Ace ace) {
-        if (!port.isPortSecurityEnabled()) {
+        if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
             return false;
         }
-        programAceRule(port, NwConstants.ADD_FLOW, aclName, ace, null);
-        updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
+        programAceRule(port, ace, NwConstants.ADD_FLOW);
+        // TODO: If this is the first port on the DPN for a remote ACL, add
+        // remote ACL flows.
+        // updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
         return true;
     }
 
     @Override
     public boolean removeAce(AclInterface port, String aclName, Ace ace) {
-        if (!port.isPortSecurityEnabled()) {
+        if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
             return false;
         }
-        programAceRule(port, NwConstants.DEL_FLOW, aclName, ace, null);
-        updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
+        programAceRule(port, ace, NwConstants.MOD_FLOW);
+        // TODO: If this is the last port on the DPN for a remote ACL, delete
+        // remote ACL flows.
+        // updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
         return true;
     }
 
@@ -276,15 +504,14 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
     /**
      * Program the default anti-spoofing rules.
      *
-     * @param dpid the dpid
+     * @param port the acl interface
      * @param dhcpMacAddress the dhcp mac address.
      * @param allowedAddresses the allowed addresses
-     * @param lportTag the lport tag
      * @param action add/modify/remove action
      * @param addOrRemove addorRemove
      */
-    protected abstract void programGeneralFixedRules(BigInteger dpid, String dhcpMacAddress,
-            List<AllowedAddressPairs> allowedAddresses, int lportTag, Action action, int addOrRemove);
+    protected abstract void programGeneralFixedRules(AclInterface port, String dhcpMacAddress,
+            List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove);
 
     /**
      * Update arp for allowed address pairs.
@@ -298,40 +525,12 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
             List<AllowedAddressPairs> deletedAAP, List<AllowedAddressPairs> addedAAP);
 
     /**
-     * Program the default specific rules.
+     * Programs broadcast rules.
      *
-     * @param dpid the dpid
-     * @param dhcpMacAddress the dhcp mac address.
-     * @param allowedAddresses the allowed addresses
-     * @param lportTag the lport tag
-     * @param portId the port id
-     * @param action add/modify/remove action
-     * @param addOrRemove addorRemove
-     */
-    protected abstract void programSpecificFixedRules(BigInteger dpid, String dhcpMacAddress,
-            List<AllowedAddressPairs> allowedAddresses, int lportTag, String portId, Action action, int addOrRemove);
-
-    /**
-     * Programs the acl custom rules.
-     *
-     * @param port acl interface
-     * @param aclUuidList the list of acl uuid to be applied
+     * @param port the Acl Interface port
      * @param addOrRemove whether to delete or add flow
-     * @return program succeeded
      */
-    protected abstract boolean programAclRules(AclInterface port, List<Uuid> aclUuidList, int addOrRemove);
-
-    /**
-     * Programs the ace custom rule.
-     *
-     * @param port acl interface
-     * @param addOrRemove whether to delete or add flow
-     * @param aclName the acl name
-     * @param ace rule to be program
-     * @param syncAllowedAddresses the allowed addresses
-     */
-    protected abstract void programAceRule(AclInterface port, int addOrRemove, String aclName, Ace ace,
-            List<AllowedAddressPairs> syncAllowedAddresses);
+    protected abstract void programBroadcastRules(AclInterface port, int addOrRemove);
 
     /**
      * Writes/remove the flow to/from the datastore.
@@ -362,26 +561,27 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
     protected void syncFlow(BigInteger dpId, short tableId, String flowId, int priority, String flowName,
             int idleTimeOut, int hardTimeOut, BigInteger cookie, List<? extends MatchInfoBase> matches,
             List<InstructionInfo> instructions, int addOrRemove) {
-        DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
-        dataStoreCoordinator.enqueueJob(flowName, () -> {
-            List<ListenableFuture<Void>> futures = new ArrayList<>();
+        jobCoordinator.enqueueJob(flowName, () -> {
             if (addOrRemove == NwConstants.DEL_FLOW) {
                 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
                         idleTimeOut, hardTimeOut, cookie, matches, null);
                 LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
 
-                futures.add(mdsalManager.removeFlow(dpId, flowEntity));
+                return Collections.singletonList(mdsalManager.removeFlow(dpId, flowEntity));
 
             } else {
                 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
                         idleTimeOut, hardTimeOut, cookie, matches, instructions);
                 LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
-                futures.add(mdsalManager.installFlow(dpId, flowEntity));
+                return Collections.singletonList(mdsalManager.installFlow(dpId, flowEntity));
             }
-            return futures;
         });
     }
 
+    protected List<InstructionInfo> getDispatcherTableResubmitInstructions() {
+        return getDispatcherTableResubmitInstructions(new ArrayList<>());
+    }
+
     /**
      * Gets the dispatcher table resubmit instructions based on ingress/egress service mode w.r.t switch.
      *
@@ -402,164 +602,63 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
     }
 
     private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove) {
-        updateRemoteAclFilterTable(port, addOrRemove, false);
+        updateRemoteAclFilterTable(port, port.getSecurityGroups(), port.getAllowedAddressPairs(), addOrRemove);
     }
 
-    private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove, boolean isAclDeleted) {
-        if (port.getSecurityGroups() == null) {
+    private void updateRemoteAclFilterTable(AclInterface port, List<Uuid> aclList, List<AllowedAddressPairs> aaps,
+            int addOrRemove) {
+        if (aclList == null) {
             LOG.debug("Port {} without SGs", port.getInterfaceId());
             return;
         }
-
-        if (AclServiceUtils.exactlyOneAcl(port)) {
-            Uuid acl = port.getSecurityGroups().get(0);
-            BigInteger aclId = aclServiceUtils.buildAclId(acl);
+        for (Uuid acl : aclList) {
             if (aclDataUtil.getRemoteAcl(acl) != null) {
                 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(acl);
                 Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
+                Integer aclTag = aclServiceUtils.getAclTag(acl);
 
-                for (AllowedAddressPairs ip : port.getAllowedAddressPairs()) {
+                for (AllowedAddressPairs ip : aaps) {
                     if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
                         continue;
                     }
                     for (BigInteger dpId : dpns) {
-                        updateRemoteAclTableForPort(port, acl, addOrRemove, ip, aclId, dpId);
+                        programRemoteAclTableFlow(dpId, aclTag, ip, addOrRemove);
                     }
                 }
-                syncRemoteAclTableFromOtherDpns(port, acl, aclId, addOrRemove);
-            } else {
-                LOG.debug("Port {} with more than one SG ({}). Don't change ACL filter table", port.getInterfaceId(),
-                        port.getSecurityGroups().size());
+                syncRemoteAclTableFromOtherDpns(port, acl, aclTag, addOrRemove);
             }
-        } else if (port.getSecurityGroups() != null && port.getSecurityGroups().size() > 1) {
-            updateRemoteAclTableForMultipleAcls(port, addOrRemove, port.getInterfaceId());
         }
-        syncRemoteAclTable(port, addOrRemove, port.getInterfaceId(), isAclDeleted);
     }
 
-    private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid acl, BigInteger aclId, int addOrRemove) {
-        List<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(acl);
+    private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid acl, Integer aclTag, int addOrRemove) {
+        Collection<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(acl);
         BigInteger dpId = port.getDpId();
         boolean isFirstPortInDpn = true;
-        for (AclInterface aclInterface : aclInterfaces) {
-            if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
-                continue;
-            }
-            if (dpId.equals(aclInterface.getDpId())) {
-                isFirstPortInDpn = false;
-                break;
-            }
-        }
-        if (isFirstPortInDpn) {
+        if (aclInterfaces != null) {
             for (AclInterface aclInterface : aclInterfaces) {
                 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
                     continue;
                 }
-                for (AllowedAddressPairs ip : aclInterface.getAllowedAddressPairs()) {
-                    updateRemoteAclTableForPort(aclInterface, acl, addOrRemove, ip, aclId, port.getDpId());
+                if (dpId.equals(aclInterface.getDpId())) {
+                    isFirstPortInDpn = false;
+                    break;
                 }
             }
-        }
-    }
-
-    private void syncRemoteAclTable(AclInterface port, int addOrRemove, String ignorePort, boolean isAclDeleted) {
-        for (Uuid aclUuid : port.getSecurityGroups()) {
-            if (aclDataUtil.getRemoteAcl(aclUuid) == null) {
-                continue;
-            }
-            List<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(aclUuid);
-            for (AclInterface aclInterface : aclInterfaces) {
-                if (aclInterface.getInterfaceId().equals(port.getInterfaceId())
-                        || AclServiceUtils.exactlyOneAcl(aclInterface)) {
-                    continue;
-                }
-                boolean allMultipleAcls = true;
-                List<Uuid> remoteInterfaceRemoteAcls = aclInterface.getSecurityGroups();
-                if (remoteInterfaceRemoteAcls != null) {
-                    for (Uuid remoteInterfaceRemoteAcl : remoteInterfaceRemoteAcls) {
-                        if (aclDataUtil.getRemoteAcl(remoteInterfaceRemoteAcl) == null) {
-                            continue;
-                        }
-                        List<AclInterface> aclInterfaces2 = aclDataUtil.getInterfaceList(remoteInterfaceRemoteAcl);
-                        for (AclInterface aclInterface2 : aclInterfaces2) {
-                            if (aclInterface2.getInterfaceId().equals(aclInterface.getInterfaceId())) {
-                                continue;
-                            }
-                            if (aclInterface2.getSecurityGroups().size() == 1) {
-                                allMultipleAcls = false;
-                                break;
-                            }
-                        }
-                    }
-                }
-                int addRremove = (allMultipleAcls) ? NwConstants.DEL_FLOW : NwConstants.ADD_FLOW;
-                addRremove = (isAclDeleted) ? NwConstants.DEL_FLOW : addRremove;
-                for (AllowedAddressPairs ip : aclInterface.getAllowedAddressPairs()) {
-                    if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
+            if (isFirstPortInDpn) {
+                for (AclInterface aclInterface : aclInterfaces) {
+                    if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
                         continue;
                     }
-                    updateRemoteAclTableForPort(aclInterface, aclUuid, addRremove, ip,
-                            aclServiceUtils.buildAclId(aclUuid), aclInterface.getDpId());
-                }
-            }
-        }
-    }
-
-    private void updateRemoteAclTableForMultipleAcls(AclInterface port, int addOrRemove, String ignorePort) {
-        BigInteger dpId = port.getDpId();
-        if (dpId == null) {
-            LOG.warn("trying to write to null dpnId");
-            return;
-        }
-        for (Uuid aclUuid : port.getSecurityGroups()) {
-            if (aclDataUtil.getRemoteAcl(aclUuid) == null) {
-                continue;
-            }
-            Acl acl = AclServiceUtils.getAcl(dataBroker, aclUuid.getValue());
-            if (null == acl) {
-                LOG.debug("The ACL {} is empty", aclUuid);
-                return;
-            }
-
-            Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(aclUuid);
-            Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
-
-            AccessListEntries accessListEntries = acl.getAccessListEntries();
-            List<Ace> aceList = accessListEntries.getAce();
-            for (Ace ace : aceList) {
-                SecurityRuleAttr aceAttr = AclServiceUtils.getAccesssListAttributes(ace);
-                if (aceAttr.getRemoteGroupId() == null) {
-                    continue;
-                }
-                List<AclInterface> interfaceList = aclDataUtil.getInterfaceList(aceAttr.getRemoteGroupId());
-                if (interfaceList == null) {
-                    continue;
-                }
-
-                for (AclInterface inter : interfaceList) {
-                    if (ignorePort.equals(inter.getInterfaceId())) {
-                        continue;
-                    }
-                    if (inter.getSecurityGroups() != null && inter.getSecurityGroups().size() == 1) {
-                        BigInteger aclId = aclServiceUtils.buildAclId(aceAttr.getRemoteGroupId());
-                        for (AllowedAddressPairs ip : port.getAllowedAddressPairs()) {
-                            if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
-                                continue;
-                            }
-                            for (BigInteger dpnId : dpns) {
-                                updateRemoteAclTableForPort(port, aceAttr.getRemoteGroupId(), addOrRemove, ip, aclId,
-                                        dpnId);
-                            }
-                        }
-                        syncRemoteAclTableFromOtherDpns(port, aclUuid, aclId, addOrRemove);
+                    for (AllowedAddressPairs ip : aclInterface.getAllowedAddressPairs()) {
+                        programRemoteAclTableFlow(port.getDpId(), aclTag, ip, addOrRemove);
                     }
                 }
             }
         }
     }
 
-    protected abstract void updateRemoteAclTableForPort(AclInterface port, Uuid acl, int addOrRemove,
-            AllowedAddressPairs ip, BigInteger aclId, BigInteger dpId);
+    protected abstract void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs ip,
+            int addOrRemove);
 
     protected String getOperAsString(int flowOper) {
         String oper;
@@ -595,12 +694,216 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
         return dpns;
     }
 
-    protected char[] getIpPrefixOrAddress(AllowedAddressPairs ip) {
-        if (ip.getIpAddress().getIpAddress() != null) {
-            return ip.getIpAddress().getIpAddress().getValue();
-        } else if (ip.getIpAddress().getIpPrefix() != null) {
-            return ip.getIpAddress().getIpPrefix().getValue();
+    /**
+     * Programs the port specific fixed rules.
+     *
+     * @param dpId the dp id
+     * @param allowedAddresses the allowed addresses
+     * @param lportTag the lport tag
+     * @param portId the portId
+     * @param action the action
+     * @param write whether to add or remove the flow.
+     */
+    protected void programAclPortSpecificFixedRules(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
+            int lportTag, String portId, Action action, int write) {
+        programGotoClassifierTableRules(dpId, allowedAddresses, lportTag, write);
+        if (action == Action.ADD || action == Action.REMOVE) {
+            programConntrackRecircRules(dpId, allowedAddresses, lportTag, portId, write);
+            programPortSpecificDropRules(dpId, lportTag, write);
+            programAclCommitRules(dpId, lportTag, portId, write);
+        }
+        LOG.info("programAclPortSpecificFixedRules: flows for dpId={}, lportId={}, action={}, write={}", dpId, lportTag,
+                action, write);
+    }
+
+    protected abstract void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps,
+            int lportTag, int addOrRemove);
+
+    /**
+     * Adds the rule to send the packet to the netfilter to check whether it is a known packet.
+     *
+     * @param dpId the dpId
+     * @param aaps the allowed address pairs
+     * @param lportTag the lport tag
+     * @param portId the portId
+     * @param addOrRemove whether to add or remove the flow
+     */
+    protected void programConntrackRecircRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
+            String portId, int addOrRemove) {
+        if (AclServiceUtils.doesIpv4AddressExists(aaps)) {
+            programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
+        }
+        if (AclServiceUtils.doesIpv6AddressExists(aaps)) {
+            programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
+        }
+    }
+
+    protected void programConntrackRecircRule(BigInteger dpId, int lportTag, String portId,
+            MatchEthernetType matchEtherType, int addOrRemove) {
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(matchEtherType);
+        matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            Long elanTag = getElanIdFromAclInterface(portId);
+            if (elanTag == null) {
+                LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={},", portId,
+                        dpId, lportTag, addOrRemove);
+                return;
+            }
+            List<ActionInfo> actionsInfos = new ArrayList<>();
+            actionsInfos.add(new ActionNxConntrack(2, 0, 0, elanTag.intValue(), getAclForExistingTrafficTable()));
+            instructions.add(new InstructionApplyActions(actionsInfos));
+        }
+
+        String flowName =
+                this.directionString + "_Fixed_Conntrk_" + dpId + "_" + lportTag + "_" + matchEtherType + "_Recirc";
+        syncFlow(dpId, getAclConntrackSenderTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
+                AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
+    }
+
+    /**
+     * Adds the rules to drop the unknown/invalid packets .
+     *
+     * @param dpId the dpId
+     * @param lportTag the lport tag
+     * @param addOrRemove whether to add or remove the flow
+     */
+    protected void programPortSpecificDropRules(BigInteger dpId, int lportTag, int addOrRemove) {
+        LOG.debug("Programming Drop Rules: DpId={}, lportTag={}, addOrRemove={}", dpId, lportTag, addOrRemove);
+        programConntrackInvalidDropRule(dpId, lportTag, addOrRemove);
+        programAclRuleMissDropRule(dpId, lportTag, addOrRemove);
+    }
+
+    /**
+     * Adds the rule to drop the conntrack invalid packets .
+     *
+     * @param dpId the dpId
+     * @param lportTag the lport tag
+     * @param addOrRemove whether to add or remove the flow
+     */
+    protected void programConntrackInvalidDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
+        List<MatchInfoBase> matches = AclServiceOFFlowBuilder.addLPortTagMatches(lportTag,
+                AclConstants.TRACKED_INV_CT_STATE, AclConstants.TRACKED_INV_CT_STATE_MASK, serviceMode);
+        List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
+
+        String flowId = this.directionString + "_Fixed_Conntrk_Drop" + dpId + "_" + lportTag + "_Tracked_Invalid";
+        syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, "ACL",
+                0, 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
+    }
+
+    /**
+     * Program ACL rule miss drop rule for a port.
+     *
+     * @param dpId the dp id
+     * @param lportTag the lport tag
+     * @param addOrRemove the add or remove
+     */
+    protected void programAclRuleMissDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
+        List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
+
+        String flowId = this.directionString + "_Fixed_Acl_Rule_Miss_Drop_" + dpId + "_" + lportTag;
+        syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.CT_STATE_TRACKED_NEW_DROP_PRIORITY, "ACL",
+                0, 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
+    }
+
+    /**
+     * Program acl commit rules.
+     *
+     * @param dpId the dp id
+     * @param lportTag the lport tag
+     * @param portId the port id
+     * @param addOrRemove the add or remove
+     */
+    protected void programAclCommitRules(BigInteger dpId, int lportTag, String portId, int addOrRemove) {
+        programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
+        programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
+        programAclCommitRuleForNonConntrack(dpId, lportTag, addOrRemove);
+    }
+
+    /**
+     * Program acl commit rule for conntrack.
+     *
+     * @param dpId the dp id
+     * @param lportTag the lport tag
+     * @param portId the port id
+     * @param matchEtherType the match ether type
+     * @param addOrRemove the add or remove
+     */
+    protected void programAclCommitRuleForConntrack(BigInteger dpId, int lportTag, String portId,
+            MatchEthernetType matchEtherType, int addOrRemove) {
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(matchEtherType);
+        matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
+        matches.add(
+                AclServiceUtils.buildAclConntrackClassifierTypeMatch(AclConntrackClassifierType.CONNTRACK_SUPPORTED));
+
+        List<ActionInfo> actionsInfos = new ArrayList<>();
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            Long elanId = getElanIdFromAclInterface(portId);
+            if (elanId == null) {
+                LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={}", portId, dpId,
+                        lportTag, addOrRemove);
+                return;
+            }
+            List<NxCtAction> ctActionsList =
+                    Lists.newArrayList(new ActionNxConntrack.NxCtMark(AclConstants.CT_MARK_EST_STATE));
+            actionsInfos.add(new ActionNxConntrack(2, 1, 0, elanId.intValue(), (short) 255, ctActionsList));
+        }
+        List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
+
+        String flowName = directionString + "_Acl_Commit_Conntrack_" + dpId + "_" + lportTag + "_" + matchEtherType;
+        // Flow for conntrack traffic to commit and resubmit to dispatcher
+        syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
+                AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
+    }
+
+    /**
+     * Program acl commit rule for non conntrack.
+     *
+     * @param dpId the dp id
+     * @param lportTag the lport tag
+     * @param addOrRemove the add or remove
+     */
+    protected void programAclCommitRuleForNonConntrack(BigInteger dpId, int lportTag, int addOrRemove) {
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
+        matches.add(AclServiceUtils
+                .buildAclConntrackClassifierTypeMatch(AclConntrackClassifierType.NON_CONNTRACK_SUPPORTED));
+
+        List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
+        String flowName = this.directionString + "_Acl_Commit_Non_Conntrack_" + dpId + "_" + lportTag;
+        // Flow for non-conntrack traffic to resubmit to dispatcher
+        syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
+                AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
+    }
+
+    protected Long getElanIdFromAclInterface(String elanInterfaceName) {
+        AclInterface aclInterface = aclInterfaceCache.get(elanInterfaceName);
+        if (null != aclInterface) {
+            return aclInterface.getElanId();
         }
         return null;
     }
+
+    protected abstract boolean isValidDirection(Class<? extends DirectionBase> direction);
+
+    protected abstract short getAclAntiSpoofingTable();
+
+    protected abstract short getAclConntrackClassifierTable();
+
+    protected abstract short getAclConntrackSenderTable();
+
+    protected abstract short getAclForExistingTrafficTable();
+
+    protected abstract short getAclFilterCumDispatcherTable();
+
+    protected abstract short getAclRuleBasedFilterTable();
+
+    protected abstract short getAclRemoteAclTable();
+
+    protected abstract short getAclCommitterTable();
 }