ACL: Support for non-conntrack supported traffic.
[netvirt.git] / vpnservice / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / AbstractAclServiceImpl.java
index c67d1559c036c81c8c63463bb848785688241adc..8b67508feb744ef13ea2e1b7e8419b215d977eea 100644 (file)
@@ -7,30 +7,59 @@
  */
 package org.opendaylight.netvirt.aclservice;
 
+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.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
-import org.opendaylight.genius.mdsalutil.ActionType;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.InstructionInfo;
-import org.opendaylight.genius.mdsalutil.InstructionType;
 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.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;
 import org.slf4j.LoggerFactory;
@@ -41,23 +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 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) {
+            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
@@ -67,36 +115,70 @@ 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(dpId, port.getAllowedAddressPairs(), port.getLPortTag(), port.getSecurityGroups(),
-                Action.ADD, NwConstants.ADD_FLOW, port.getInterfaceId());
+        LOG.debug("Applying ACL on port {} with DpId {}", port, dpId);
+        programAcl(port, Action.ADD, NwConstants.ADD_FLOW);
+        updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
+        return true;
+    }
 
-        bindService(port.getInterfaceId());
+    @Override
+    public boolean bindAcl(AclInterface port) {
+        if (port == null || port.getSecurityGroups() == null) {
+            LOG.error("Port and port security groups cannot be null for binding ACL service, port={}", port);
+            return false;
+        }
+        bindService(port);
+        if (port.getDpId() != null) {
+            updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean unbindAcl(AclInterface port) {
+        if (port == null) {
+            LOG.error("Port cannot be null for unbinding ACL service");
+            return false;
+        }
+        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);
             } else {
-                result = removeAcl(portAfter);
+                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;
@@ -104,77 +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 (addedAllowedAddressPairs != null && !addedAllowedAddressPairs.isEmpty()) {
-            programAclWithAllowedAddress(dpId, addedAllowedAddressPairs, portAfter.getLPortTag(),
-                    portAfter.getSecurityGroups(), Action.UPDATE, NwConstants.ADD_FLOW, portAfter.getInterfaceId());
+        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 (addedAaps != null && !addedAaps.isEmpty()) {
+            programAclWithAllowedAddress(portAfter, addedAaps, Action.UPDATE, NwConstants.ADD_FLOW);
+            updateRemoteAclFilterTable(portAfter, portAfter.getSecurityGroups(), addedAaps, NwConstants.ADD_FLOW);
         }
-        if (deletedAllowedAddressPairs != null && !deletedAllowedAddressPairs.isEmpty()) {
-            programAclWithAllowedAddress(dpId, deletedAllowedAddressPairs, portAfter.getLPortTag(),
-                    portAfter.getSecurityGroups(), Action.UPDATE, NwConstants.DEL_FLOW, portAfter.getInterfaceId());
+        updateArpForAllowedAddressPairs(dpId, portAfter.getLPortTag(), deletedAaps, portAfter.getAllowedAddressPairs());
+        if (portAfter.getSubnetIpPrefixes() != null && portBefore.getSubnetIpPrefixes() == null) {
+            programBroadcastRules(portAfter, NwConstants.ADD_FLOW);
         }
 
         List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(portAfter.getSecurityGroups(),
                 portBefore.getSecurityGroups());
         List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(portBefore.getSecurityGroups(),
                 portAfter.getSecurityGroups());
-        if (deletedAcls != null && !deletedAcls.isEmpty()) {
-            updateCustomRules(dpId, portAfter.getLPortTag(), deletedAcls, NwConstants.DEL_FLOW,
-                    portAfter.getInterfaceId(), portAfter.getAllowedAddressPairs());
-        }
-        if (addedAcls != null && !addedAcls.isEmpty()) {
-            updateCustomRules(dpId, portAfter.getLPortTag(), addedAcls, NwConstants.ADD_FLOW,
-                    portAfter.getInterfaceId(), portAfter.getAllowedAddressPairs());
+        if (deletedAcls.isEmpty() && addedAcls.isEmpty()) {
+            LOG.trace("No change w.r.t ACL list for port={}", portAfter.getInterfaceId());
+            return;
         }
+
+        handleAclChange(portBefore, deletedAcls, NwConstants.DEL_FLOW);
+        handleAclChange(portAfter, addedAcls, NwConstants.ADD_FLOW);
+    }
+
+    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(BigInteger dpId, int lportTag, List<Uuid> aclUuidList, int action,
-                                   String portId, List<AllowedAddressPairs> syncAllowedAddresses) {
-        programAclRules(aclUuidList, dpId, lportTag, action, portId);
-        syncRemoteAclRules(aclUuidList, action, portId, 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;
         }
-        for (Uuid remoteAclId : aclUuidList) {
-            Set<AclInterface> portSet = AclDataUtil.getRemoteAclInterfaces(remoteAclId);
-            if (portSet == null) {
+        Integer firstRemoteAclTag = remoteAclTags.first();
+        Integer lastRemoteAclTag = remoteAclTags.last();
+
+        programFirstRemoteAclEntryInDispatcherTable(port, firstRemoteAclTag, addOrRemove);
+        programLastRemoteAclEntryInDispatcherTable(port, lastRemoteAclTag, addOrRemove);
+
+        Integer previousRemoteAclTag = firstRemoteAclTag;
+        for (Integer remoteAclTag : remoteAclTags) {
+            if (remoteAclTag.equals(firstRemoteAclTag)) {
                 continue;
             }
-            for (AclInterface port : portSet) {
-                if (currentPortId.equals(port.getInterfaceId())) {
-                    continue;
-                }
-                List<Ace> remoteAceList = AclServiceUtils.getAceWithRemoteAclId(dataBroker, port, remoteAclId);
-                for (Ace ace : remoteAceList) {
-                    programAceRule(port.getDpId(), port.getLPortTag(), action, ace, port.getInterfaceId(),
-                            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;
         }
     }
 
-    private void programAclWithAllowedAddress(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
-                                              int lportTag, List<Uuid> aclUuidList, Action action, int addOrRemove,
-                                              String portId) {
-        programGeneralFixedRules(dpId, "", allowedAddresses, lportTag, action, addOrRemove);
-        programSpecificFixedRules(dpId, "", allowedAddresses, lportTag, portId, action, addOrRemove);
+    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();
+        LOG.debug("Applying ACL Allowed Address on DpId {}, lportTag {}, Action {}", dpId, lportTag, action);
+        String portId = port.getInterfaceId();
+        programGeneralFixedRules(port, "", allowedAddresses, action, addOrRemove);
+        programAclPortSpecificFixedRules(dpId, allowedAddresses, lportTag, portId, action, addOrRemove);
         if (action == Action.ADD || action == Action.REMOVE) {
-            programAclRules(aclUuidList, dpId, lportTag, addOrRemove, portId);
+            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();
@@ -182,101 +458,79 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
             LOG.error("Unable to find DP Id from ACL interface with id {}", port.getInterfaceId());
             return false;
         }
-        programAclWithAllowedAddress(dpId, port.getAllowedAddressPairs(), port.getLPortTag(), port.getSecurityGroups(),
-                Action.REMOVE, NwConstants.DEL_FLOW, port.getInterfaceId());
-
-        unbindService(port.getInterfaceId());
+        programAcl(port, Action.REMOVE, NwConstants.DEL_FLOW);
+        updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
         return true;
     }
 
     @Override
-    public boolean applyAce(AclInterface port, Ace ace) {
-        if (!port.isPortSecurityEnabled()) {
+    public boolean applyAce(AclInterface port, String aclName, Ace ace) {
+        if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
             return false;
         }
-        programAceRule(port.getDpId(), port.getLPortTag(), NwConstants.ADD_FLOW, ace,
-                port.getInterfaceId(), null);
+        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, Ace ace) {
-        if (!port.isPortSecurityEnabled()) {
+    public boolean removeAce(AclInterface port, String aclName, Ace ace) {
+        if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
             return false;
         }
-        programAceRule(port.getDpId(), port.getLPortTag(), NwConstants.DEL_FLOW, ace,
-                port.getInterfaceId(), null);
+        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;
     }
 
     /**
      * Bind service.
      *
-     * @param interfaceName
-     *            the interface name
+     * @param aclInterface the acl interface
      */
-    protected abstract void bindService(String interfaceName);
+    public abstract void bindService(AclInterface aclInterface);
 
     /**
      * Unbind service.
      *
-     * @param interfaceName
-     *            the interface name
+     * @param aclInterface the acl interface
      */
-    protected abstract void unbindService(String interfaceName);
+    protected abstract void unbindService(AclInterface aclInterface);
 
     /**
      * 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);
 
     /**
-     * Program the default specific rules.
+     * Update arp for allowed address pairs.
      *
-     * @param dpid the dpid
-     * @param dhcpMacAddress the dhcp mac address.
-     * @param allowedAddresses the allowed addresses
+     * @param dpId the dp id
      * @param lportTag the lport tag
-     * @param portId the port id
-     * @param action add/modify/remove action
-     * @param addOrRemove addorRemove
+     * @param deletedAAP the deleted allowed address pairs
+     * @param addedAAP the added allowed address pairs
      */
-    protected abstract void programSpecificFixedRules(BigInteger dpid, String dhcpMacAddress,
-            List<AllowedAddressPairs> allowedAddresses, int lportTag, String portId, Action action, int addOrRemove);
+    protected abstract void updateArpForAllowedAddressPairs(BigInteger dpId, int lportTag,
+            List<AllowedAddressPairs> deletedAAP, List<AllowedAddressPairs> addedAAP);
 
     /**
-     * Programs the acl custom rules.
+     * Programs broadcast rules.
      *
-     * @param aclUuidList the list of acl uuid to be applied
-     * @param dpId the dpId
-     * @param lportTag the lport tag
-     * @param addOrRemove whether to delete or add flow
-     * @param portId the port id
-     * @return program succeeded
-     */
-    protected abstract boolean programAclRules(List<Uuid> aclUuidList, BigInteger dpId, int lportTag, int addOrRemove,
-                                            String portId);
-
-    /**
-     * Programs the ace custom rule.
-     *
-     * @param dpId the dpId
-     * @param lportTag the lport tag
+     * @param port the Acl Interface port
      * @param addOrRemove whether to delete or add flow
-     * @param ace rule to be program
-     * @param portId the port id
-     * @param syncAllowedAddresses the allowed addresses
      */
-    protected abstract void programAceRule(BigInteger dpId, int lportTag, int addOrRemove, Ace ace, String portId,
-                                           List<AllowedAddressPairs> syncAllowedAddresses);
+    protected abstract void programBroadcastRules(AclInterface port, int addOrRemove);
 
     /**
      * Writes/remove the flow to/from the datastore.
@@ -307,24 +561,32 @@ 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) {
-        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);
-            mdsalManager.removeFlow(flowEntity);
-        } else {
-            FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName, idleTimeOut,
-                    hardTimeOut, cookie, matches, instructions);
-            LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
-            mdsalManager.installFlow(flowEntity);
-        }
+        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);
+
+                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);
+                return Collections.singletonList(mdsalManager.installFlow(dpId, flowEntity));
+            }
+        });
+    }
+
+    protected List<InstructionInfo> getDispatcherTableResubmitInstructions() {
+        return getDispatcherTableResubmitInstructions(new ArrayList<>());
     }
 
     /**
-     * Gets the dispatcher table resubmit instructions based on ingress/egress
-     * service mode w.r.t switch.
+     * Gets the dispatcher table resubmit instructions based on ingress/egress service mode w.r.t switch.
      *
-     * @param actionsInfos the actions infos
+     * @param actionsInfos
+     *            the actions infos
      * @return the instructions for dispatcher table resubmit
      */
     protected List<InstructionInfo> getDispatcherTableResubmitInstructions(List<ActionInfo> actionsInfos) {
@@ -334,11 +596,70 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
         }
 
         List<InstructionInfo> instructions = new ArrayList<>();
-        actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] {Short.toString(dispatcherTableId)}));
-        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        actionsInfos.add(new ActionNxResubmit(dispatcherTableId));
+        instructions.add(new InstructionApplyActions(actionsInfos));
         return instructions;
     }
 
+    private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove) {
+        updateRemoteAclFilterTable(port, port.getSecurityGroups(), port.getAllowedAddressPairs(), addOrRemove);
+    }
+
+    private void updateRemoteAclFilterTable(AclInterface port, List<Uuid> aclList, List<AllowedAddressPairs> aaps,
+            int addOrRemove) {
+        if (aclList == null) {
+            LOG.debug("Port {} without SGs", port.getInterfaceId());
+            return;
+        }
+        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 : aaps) {
+                    if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
+                        continue;
+                    }
+                    for (BigInteger dpId : dpns) {
+                        programRemoteAclTableFlow(dpId, aclTag, ip, addOrRemove);
+                    }
+                }
+                syncRemoteAclTableFromOtherDpns(port, acl, aclTag, addOrRemove);
+            }
+        }
+    }
+
+    private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid acl, Integer aclTag, int addOrRemove) {
+        Collection<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(acl);
+        BigInteger dpId = port.getDpId();
+        boolean isFirstPortInDpn = true;
+        if (aclInterfaces != null) {
+            for (AclInterface aclInterface : aclInterfaces) {
+                if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
+                    continue;
+                }
+                if (dpId.equals(aclInterface.getDpId())) {
+                    isFirstPortInDpn = false;
+                    break;
+                }
+            }
+            if (isFirstPortInDpn) {
+                for (AclInterface aclInterface : aclInterfaces) {
+                    if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
+                        continue;
+                    }
+                    for (AllowedAddressPairs ip : aclInterface.getAllowedAddressPairs()) {
+                        programRemoteAclTableFlow(port.getDpId(), aclTag, ip, addOrRemove);
+                    }
+                }
+            }
+        }
+    }
+
+    protected abstract void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs ip,
+            int addOrRemove);
+
     protected String getOperAsString(int flowOper) {
         String oper;
         switch (flowOper) {
@@ -357,4 +678,232 @@ public abstract class AbstractAclServiceImpl implements AclServiceListener {
         return oper;
     }
 
+    protected Set<BigInteger> collectDpns(Map<String, Set<AclInterface>> mapAclWithPortSet) {
+        Set<BigInteger> dpns = new HashSet<>();
+        if (mapAclWithPortSet == null) {
+            return dpns;
+        }
+        for (Set<AclInterface> innerSet : mapAclWithPortSet.values()) {
+            if (innerSet == null) {
+                continue;
+            }
+            for (AclInterface inter : innerSet) {
+                dpns.add(inter.getDpId());
+            }
+        }
+        return dpns;
+    }
+
+    /**
+     * 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();
 }