diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 2e417abe409859f76d25dde11ccc98c1b956eb0d..65f3ca440cba18c680e00de60ab9293b8a3d2a11 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -29,6 +29,7 @@ from .p4_dummy_l1.p4_dummy_l1_service_handler import P4DummyL1ServiceHandler
 from .p4_fabric_tna_int.p4_fabric_tna_int_service_handler import P4FabricINTServiceHandler
 from .p4_fabric_tna_l2_simple.p4_fabric_tna_l2_simple_service_handler import P4FabricL2SimpleServiceHandler
 from .p4_fabric_tna_l3.p4_fabric_tna_l3_service_handler import P4FabricL3ServiceHandler
+from .p4_fabric_tna_acl.p4_fabric_tna_acl_service_handler import P4FabricACLServiceHandler
 from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
 from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler
 from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler
@@ -132,6 +133,12 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.DEVICE_DRIVER: DeviceDriverEnum.DEVICEDRIVER_P4,
         }
     ]),
+    (P4FabricACLServiceHandler, [
+        {
+            FilterFieldEnum.SERVICE_TYPE: ServiceTypeEnum.SERVICETYPE_ACL,
+            FilterFieldEnum.DEVICE_DRIVER: DeviceDriverEnum.DEVICEDRIVER_P4,
+        }
+    ]),
     (L2NM_IETFL2VPN_ServiceHandler, [
         {
             FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_L2NM,
diff --git a/src/service/service/service_handlers/p4_fabric_tna_acl/__init__.py b/src/service/service/service_handlers/p4_fabric_tna_acl/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..023830645e0fcb60e3f8583674a954810af222f2
--- /dev/null
+++ b/src/service/service/service_handlers/p4_fabric_tna_acl/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/src/service/service/service_handlers/p4_fabric_tna_acl/p4_fabric_tna_acl_config.py b/src/service/service/service_handlers/p4_fabric_tna_acl/p4_fabric_tna_acl_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..09dbcc5aaae818388bb2033d6953ab69cde8f098
--- /dev/null
+++ b/src/service/service/service_handlers/p4_fabric_tna_acl/p4_fabric_tna_acl_config.py
@@ -0,0 +1,39 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Common objects and methods for In-band Network Telemetry (INT) dataplane
+based on the SD-Fabric dataplane model.
+This dataplane covers both software based and hardware-based Stratum-enabled P4 switches,
+such as the BMv2 software switch and Intel's Tofino/Tofino-2 switches.
+
+SD-Fabric repo: https://github.com/stratum/fabric-tna
+SD-Fabric docs: https://docs.sd-fabric.org/master/index.html
+"""
+
+import logging
+
+from service.service.service_handlers.p4_fabric_tna_commons.p4_fabric_tna_commons import *
+
+LOGGER = logging.getLogger(__name__)
+
+# ACL service handler settings
+ACL = "acl"
+ACTION = "action"
+ACTION_DROP = "drop"
+ACTION_ALLOW = "allow"
+ACTION_LIST = [ACTION_ALLOW, ACTION_DROP]
+
+def is_valid_acl_action(action : str) -> bool:
+    return action in ACTION_LIST
diff --git a/src/service/service/service_handlers/p4_fabric_tna_acl/p4_fabric_tna_acl_service_handler.py b/src/service/service/service_handlers/p4_fabric_tna_acl/p4_fabric_tna_acl_service_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b44a1ce8b62257d78afd5682fc16b1fcb4b739a
--- /dev/null
+++ b/src/service/service/service_handlers/p4_fabric_tna_acl/p4_fabric_tna_acl_service_handler.py
@@ -0,0 +1,526 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Service handler for P4-based access control using the SD-Fabric P4 dataplane
+for BMv2 and Intel Tofino switches.
+"""
+
+import logging
+from typing import Any, List, Dict, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigActionEnum, DeviceId, Service, Device
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_type, chk_address_ipv4, chk_prefix_len_ipv4,\
+    chk_transport_port
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
+from service.service.service_handlers.p4_fabric_tna_commons.p4_fabric_tna_commons import *
+from service.service.task_scheduler.TaskExecutor import TaskExecutor
+
+from .p4_fabric_tna_acl_config import *
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'p4_fabric_tna_acl'})
+
+class P4FabricACLServiceHandler(_ServiceHandler):
+    def __init__(   # pylint: disable=super-init-not-called
+        self, service : Service, task_executor : TaskExecutor, **settings # type: ignore
+    ) -> None:
+        """ Initialize Driver.
+            Parameters:
+                service
+                    The service instance (gRPC message) to be managed.
+                task_executor
+                    An instance of Task Executor providing access to the
+                    service handlers factory, the context and device clients,
+                    and an internal cache of already-loaded gRPC entities.
+                **settings
+                    Extra settings required by the service handler.
+
+        """
+        self.__service_label = "P4 Access Control connectivity service"
+        self.__service = service
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(self.__service.service_config, **settings)
+
+        self._init_settings()
+        self._parse_settings()
+        self._print_settings()
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]],
+        connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        """ Create/Update service endpoints from a list.
+            Parameters:
+                endpoints: List[Tuple[str, str, Optional[str]]]
+                    List of tuples, each containing a device_uuid,
+                    endpoint_uuid and, optionally, the topology_uuid
+                    of the endpoint to be added.
+                connection_uuid : Optional[str]
+                    If specified, is the UUID of the connection this endpoint is associated to.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for endpoint changes requested.
+                    Return values must be in the same order as the requested
+                    endpoints. If an endpoint is properly added, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0: return []
+
+        LOGGER.info("{} - Provision service configuration".format(
+            self.__service_label))
+
+        visited = set()
+        results = []
+        for endpoint in endpoints:
+            device_uuid, endpoint_uuid = endpoint[0:2]
+            device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            device_name = device.name
+
+            LOGGER.info("Device {}".format(device_name))
+            LOGGER.info("\t | Service endpoint UUID: {}".format(endpoint_uuid))
+
+            port_id = find_port_id_in_endpoint_list(device.device_endpoints, endpoint_uuid)
+            LOGGER.info("\t | Service port ID: {}".format(port_id))
+
+            try:
+                # Check if this port is part of the ACL configuration
+                _ = self._get_switch_port_in_port_map(device_name, port_id)
+            except Exception:
+                LOGGER.warning("Switch {} endpoint {} is not part of the ACL configuration".format(device_name, port_id))
+                results.append(False)
+                continue
+
+            dev_port_key = device_name + "-" + PORT_PREFIX + str(port_id)
+
+            # Skip already visited device ports
+            if dev_port_key in visited:
+                continue
+
+            rules = []
+            actual_rules = -1
+            applied_rules, failed_rules = 0, -1
+
+            # Create and apply rules
+            try:
+                rules = self._create_rules(
+                    device_obj=device, port_id=port_id, action=ConfigActionEnum.CONFIGACTION_SET)
+                actual_rules = len(rules)
+                applied_rules, failed_rules = apply_rules(
+                    task_executor=self.__task_executor,
+                    device_obj=device,
+                    json_config_rules=rules
+                )
+            except Exception as ex:
+                LOGGER.error("Failed to insert ACL rules on device {} due to {}".format(device.name, ex))
+            finally:
+                rules.clear()
+
+            # Ensure correct status
+            results.append(True) if (failed_rules == 0) and (applied_rules == actual_rules) \
+                else results.append(False)
+
+            # You should no longer visit this device port again
+            visited.add(dev_port_key)
+
+            LOGGER.info("Installed {}/{} ACL rules on device {} and port {}".format(
+                applied_rules, actual_rules, device_name, port_id))
+
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]],
+        connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        """ Delete service endpoints from a list.
+            Parameters:
+                endpoints: List[Tuple[str, str, Optional[str]]]
+                    List of tuples, each containing a device_uuid,
+                    endpoint_uuid, and the topology_uuid of the endpoint
+                    to be removed.
+                connection_uuid : Optional[str]
+                    If specified, is the UUID of the connection this endpoint is associated to.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for endpoint deletions requested.
+                    Return values must be in the same order as the requested
+                    endpoints. If an endpoint is properly deleted, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0: return []
+
+        LOGGER.info("{} - Deprovision service configuration".format(
+            self.__service_label))
+
+        visited = set()
+        results = []
+        for endpoint in endpoints:
+            device_uuid, endpoint_uuid = endpoint[0:2]
+            device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            device_name = device.name
+
+            LOGGER.info("Device {}".format(device_name))
+            LOGGER.info("\t | Service endpoint UUID: {}".format(endpoint_uuid))
+
+            port_id = find_port_id_in_endpoint_list(device.device_endpoints, endpoint_uuid)
+            LOGGER.info("\t | Service port ID: {}".format(port_id))
+
+            try:
+                # Check if this port is part of the ACL configuration
+                _ = self._get_switch_port_in_port_map(device_name, port_id)
+            except Exception:
+                LOGGER.warning("Switch {} endpoint {} is not part of the ACL configuration".format(device_name, port_id))
+                results.append(False)
+                continue
+
+            dev_port_key = device_name + "-" + PORT_PREFIX + str(port_id)
+
+            # Skip already visited device ports
+            if dev_port_key in visited:
+                continue
+
+            rules = []
+            actual_rules = -1
+            applied_rules, failed_rules = 0, -1
+
+            # Create and apply rules
+            try:
+                rules = self._create_rules(
+                    device_obj=device, port_id=port_id, action=ConfigActionEnum.CONFIGACTION_DELETE)
+                actual_rules = len(rules)
+                applied_rules, failed_rules = apply_rules(
+                    task_executor=self.__task_executor,
+                    device_obj=device,
+                    json_config_rules=rules
+                )
+            except Exception as ex:
+                LOGGER.error("Failed to insert ACL rules on device {} due to {}".format(device.name, ex))
+            finally:
+                rules.clear()
+
+            # Ensure correct status
+            results.append(True) if (failed_rules == 0) and (applied_rules == actual_rules) \
+                else results.append(False)
+
+            # You should no longer visit this device port again
+            visited.add(dev_port_key)
+
+            LOGGER.info("Deleted {}/{} ACL rules from device {} and port {}".format(
+                applied_rules, actual_rules, device_name, port_id))
+
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConstraint(self, constraints: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Create/Update service constraints.
+            Parameters:
+                constraints: List[Tuple[str, Any]]
+                    List of tuples, each containing a constraint_type and the
+                    new constraint_value to be set.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for constraint changes requested.
+                    Return values must be in the same order as the requested
+                    constraints. If a constraint is properly set, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Delete service constraints.
+            Parameters:
+                constraints: List[Tuple[str, Any]]
+                    List of tuples, each containing a constraint_type pointing
+                    to the constraint to be deleted, and a constraint_value
+                    containing possible additionally required values to locate
+                    the constraint to be removed.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for constraint deletions requested.
+                    Return values must be in the same order as the requested
+                    constraints. If a constraint is properly deleted, True must
+                    be returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConfig(self, resources: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Create/Update configuration for a list of service resources.
+            Parameters:
+                resources: List[Tuple[str, Any]]
+                    List of tuples, each containing a resource_key pointing to
+                    the resource to be modified, and a resource_value
+                    containing the new value to be set.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for resource key changes requested.
+                    Return values must be in the same order as the requested
+                    resource keys. If a resource is properly set, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        msg = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(resources)))
+        return [True for _ in range(len(resources))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConfig(self, resources: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Delete configuration for a list of service resources.
+            Parameters:
+                resources: List[Tuple[str, Any]]
+                    List of tuples, each containing a resource_key pointing to
+                    the resource to be modified, and a resource_value containing
+                    possible additionally required values to locate the value
+                    to be removed.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for resource key deletions requested.
+                    Return values must be in the same order as the requested
+                    resource keys. If a resource is properly deleted, True must
+                    be returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        msg = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(resources)))
+        return [True for _ in range(len(resources))]
+
+    def _init_settings(self):
+        self.__switch_info = {}
+        self.__port_map = {}
+
+        try:
+            self.__settings = self.__settings_handler.get('/settings')
+            LOGGER.info("{} with settings: {}".format(self.__service_label, self.__settings))
+        except Exception as ex:
+            self.__settings = {}
+            LOGGER.error("Failed to parse service settings: {}".format(ex))
+
+    def _default_settings(self):
+        acl = [
+            {
+                PORT_ID: 1,
+                IPV4_SRC: "10.158.72.11",
+                IPV4_PREFIX_LEN: 32,
+                ACTION: ACTION_DROP
+            },
+            {
+                PORT_ID: 1,
+                TRN_PORT_DST: 8080,
+                ACTION: ACTION_DROP
+            }
+        ]
+
+        switch_info = {
+            "p4-sw1": {
+                ARCH: TARGET_ARCH_V1MODEL,
+                DPID: 1,
+                ACL: acl
+            }
+        }
+        self.__settings = {
+            SWITCH_INFO: switch_info
+        }
+
+        port_map = {
+            "p4-sw1": {
+                "port-1": {
+                    PORT_ID: 1,
+                    ACL: [
+                        {
+                            IPV4_SRC: "10.158.72.11",
+                            IPV4_PREFIX_LEN: 32,
+                            ACTION: ACTION_DROP
+                        },
+                        {
+                            TRN_PORT_DST: 8080,
+                            ACTION: ACTION_DROP
+                        }
+                    ]
+                }
+            }
+        }
+
+    def _parse_settings(self):
+        #TODO: Pass settings in a correct way
+        try:
+            self.__switch_info = self.__settings[SWITCH_INFO]
+        except Exception as ex:
+            LOGGER.error("Failed to parse settings: {}".format(ex))
+            self._default_settings() #TODO: Remove when bug is fixed
+            self.__switch_info = self.__settings[SWITCH_INFO]
+        assert isinstance(self.__switch_info, dict), "Switch info object must be a map with switch names as keys"
+
+        for switch_name, switch_info in self.__switch_info.items():
+            assert switch_name, "Invalid P4 switch name"
+            assert isinstance(switch_info, dict), "Switch {} info must be a map with arch, dpid, and fwd_list items)"
+            assert switch_info[ARCH] in SUPPORTED_TARGET_ARCH_LIST, \
+                "Switch {} - Supported P4 architectures are: {}".format(switch_name, ','.join(SUPPORTED_TARGET_ARCH_LIST))
+            switch_dpid = switch_info[DPID]
+            assert switch_dpid > 0, "Switch {} - P4 switch dataplane ID must be a positive integer".format(switch_name, switch_info[DPID])
+
+            # Access Control list
+            acl = switch_info[ACL]
+            assert isinstance(acl, list), "Switch {} access control list must be a list with port_id, [ipv4_dst/src, trn_post_dst/src], and action items)"
+            for acl_entry in acl:
+                LOGGER.info("ACL entry: {}".format(acl_entry))
+                port_id = acl_entry[PORT_ID]
+                assert port_id >= 0, "Switch {} - Invalid P4 switch port ID".format(switch_name)
+
+                # Prepare the port map
+                if switch_name not in self.__port_map:
+                    self.__port_map[switch_name] = {}
+                port_key = PORT_PREFIX + str(port_id)
+                if port_key not in self.__port_map[switch_name]:
+                    self.__port_map[switch_name][port_key] = {}
+                self.__port_map[switch_name][port_key][PORT_ID] = port_id
+                if ACL not in self.__port_map[switch_name][port_key]:
+                    self.__port_map[switch_name][port_key][ACL] = []
+
+                map_entry = {}
+
+                ipv4_src = ""
+                if IPV4_SRC in acl_entry:
+                    ipv4_src = acl_entry[IPV4_SRC]
+                    assert chk_address_ipv4(ipv4_src), "Invalid source IPv4 address {}".format(ipv4_dst)
+                    map_entry[IPV4_SRC] = ipv4_src
+
+                ipv4_dst = ""
+                if IPV4_DST in acl_entry:
+                    ipv4_dst = acl_entry[IPV4_DST]
+                    assert chk_address_ipv4(ipv4_dst), "Invalid destination IPv4 address {}".format(ipv4_dst)
+                    map_entry[IPV4_DST] = ipv4_dst
+
+                ipv4_prefix_len = -1
+                if ipv4_src or ipv4_dst:
+                    ipv4_prefix_len = acl_entry[IPV4_PREFIX_LEN]
+                    assert chk_prefix_len_ipv4(ipv4_prefix_len), "Invalid IPv4 address prefix length {}".format(ipv4_prefix_len)
+                    map_entry[IPV4_PREFIX_LEN] = ipv4_prefix_len
+                
+                trn_port_src = -1
+                if TRN_PORT_SRC in acl_entry:
+                    trn_port_src = acl_entry[TRN_PORT_SRC]
+                    assert chk_transport_port(trn_port_src), "Invalid source transport port"
+                    map_entry[TRN_PORT_SRC] = trn_port_src
+                
+                trn_port_dst = -1
+                if TRN_PORT_DST in acl_entry:
+                    trn_port_dst = acl_entry[TRN_PORT_DST]
+                    assert chk_transport_port(trn_port_dst), "Invalid destination transport port"
+                    map_entry[TRN_PORT_DST] = trn_port_dst
+                
+                action = acl_entry[ACTION]
+                assert is_valid_acl_action(action), "Valid actions are: {}".format(','.join(ACTION_LIST))
+
+                # Retrieve entry from the port map
+                switch_port_entry = self._get_switch_port_in_port_map(switch_name, port_id)
+
+                # Add routing entry
+                switch_port_entry[ACL].append(map_entry)
+
+    def _print_settings(self):
+        LOGGER.info("--------------- {} settings ---------------".format(self.__service.name))
+        LOGGER.info("--- Topology info")
+        for switch_name, switch_info in self.__switch_info.items():
+            LOGGER.info("\t Device {}".format(switch_name))
+            LOGGER.info("\t\t| Target P4 architecture: {}".format(switch_info[ARCH]))
+            LOGGER.info("\t\t|          Data plane ID: {}".format(switch_info[DPID]))
+            LOGGER.info("\t\t|               Port map: {}".format(self.__port_map[switch_name]))
+        LOGGER.info("-------------------------------------------------------")
+
+    def _get_switch_port_in_port_map(self, switch_name : str, port_id : int) -> Dict:
+        assert switch_name, "A valid switch name must be used as a key to the port map"
+        assert port_id > 0, "A valid switch port ID must be used as a key to a switch's port map"
+        switch_entry = self.__port_map[switch_name]
+        assert switch_entry, "Switch {} does not exist in the port map".format(switch_name)
+        port_key = PORT_PREFIX + str(port_id)
+        assert switch_entry[port_key], "Port with ID {} does not exist in the switch map".format(port_id)
+
+        return switch_entry[port_key]
+    
+    def _get_acl_of_switch_port(self, switch_name : str, port_id : int) -> List [Tuple]:
+        switch_port_entry = self._get_switch_port_in_port_map(switch_name, port_id)
+        return switch_port_entry[ACL]
+
+    def _create_rules(self, device_obj : Device, port_id : int, action : ConfigActionEnum): # type: ignore
+        dev_name = device_obj.name
+
+        rules  = []
+
+        ### ACL rules
+        acl = self._get_acl_of_switch_port(switch_name=dev_name, port_id=port_id)
+        for acl_entry in acl:
+            if IPV4_SRC in acl_entry:
+                rules += rules_set_up_acl_filter_host(
+                    ingress_port=port_id,
+                    ip_address=acl_entry[IPV4_SRC],
+                    prefix_len=acl_entry[IPV4_PREFIX_LEN],
+                    ip_direction="src",
+                    action=action
+                )
+            if IPV4_DST in acl_entry:
+                rules += rules_set_up_acl_filter_host(
+                    ingress_port=port_id,
+                    ip_address=acl_entry[IPV4_DST],
+                    prefix_len=acl_entry[IPV4_PREFIX_LEN],
+                    ip_direction="dst",
+                    action=action
+                )
+            if TRN_PORT_SRC in acl_entry:
+                rules += rules_set_up_acl_filter_port(
+                    ingress_port=port_id,
+                    transport_port=acl_entry[TRN_PORT_SRC],
+                    transport_direction="src",
+                    action=action
+                )
+            if TRN_PORT_DST in acl_entry:
+                rules += rules_set_up_acl_filter_port(
+                    ingress_port=port_id,
+                    transport_port=acl_entry[TRN_PORT_DST],
+                    transport_direction="dst",
+                    action=action
+                )
+
+        return rules
diff --git a/src/tests/p4-fabric-tna/README.md b/src/tests/p4-fabric-tna/README.md
index f6bc2dd0cfa3731da44116274c3f5dfb73d07dc8..115932bb4f7d2f3e846e8661cc7aacd9013ccc1d 100644
--- a/src/tests/p4-fabric-tna/README.md
+++ b/src/tests/p4-fabric-tna/README.md
@@ -153,6 +153,20 @@ cd ~/tfs-ctrl/
 bash src/tests/p4-fabric-tna/run_test_04b_service_deprovision_l3.sh
 ```
 
+#### Provision ACL network service via the Service API
+
+```shell
+cd ~/tfs-ctrl/
+bash src/tests/p4-fabric-tna/run_test_05a_service_provision_acl.sh
+```
+
+#### Deprovision ACL network service via the Service API
+
+```shell
+cd ~/tfs-ctrl/
+bash src/tests/p4-fabric-tna/run_test_05b_service_deprovision_acl.sh
+```
+
 #### Provision INT service via the Service API
 
 ```shell
diff --git a/src/tests/p4-fabric-tna/descriptors/service-create-acl.json b/src/tests/p4-fabric-tna/descriptors/service-create-acl.json
new file mode 100644
index 0000000000000000000000000000000000000000..d0beef01020151ff3f6cee7c6c9fba6a5ffc3b47
--- /dev/null
+++ b/src/tests/p4-fabric-tna/descriptors/service-create-acl.json
@@ -0,0 +1,65 @@
+{
+    "services": [
+        {
+            "service_id": {
+                "context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "p4-service-acl"}
+            },
+            "name": "p4-service-acl",
+            "service_type": "SERVICETYPE_ACL",
+            "service_status": {"service_status": "SERVICESTATUS_PLANNED"},
+            "service_endpoint_ids": [
+                {
+                    "device_id": {"device_uuid": {"uuid": "p4-sw1"}},
+                    "endpoint_uuid": {"uuid": "1"}
+                },
+                {
+                    "device_id": {"device_uuid": {"uuid": "p4-sw1"}},
+                    "endpoint_uuid": {"uuid": "2"}
+                }
+            ],
+            "service_config": {
+                "config_rules": [
+                    {
+                        "action": "CONFIGACTION_SET",
+                        "custom": {
+                            "resource_key": "/settings",
+                            "resource_value": {
+                                "switch_info": {
+                                    "p4-sw1": {
+                                        "arch": "v1model",
+                                        "dpid": 1,
+                                        "acl": [
+                                            {
+                                                "port_id": 1,
+                                                "trn_port_dst": 8080,
+                                                "action": "drop"
+                                            },
+                                            {
+                                                "port_id": 1,
+                                                "trn_port_src": 12345,
+                                                "action": "drop"
+                                            },
+                                            {
+                                                "port_id": 1,
+                                                "ipv4_dst": "172.16.10.10",
+                                                "ipv4_prefix_len": 32,
+                                                "action": "drop"
+                                            },
+                                            {
+                                                "port_id": 2,
+                                                "ipv4_src": "172.16.10.10",
+                                                "ipv4_prefix_len": 32,
+                                                "action": "drop"
+                                            }
+                                        ]
+                                    }
+                                }
+                            }
+                        }
+                    }
+                ]
+            },
+            "service_constraints": []
+        }
+    ]
+}
diff --git a/src/tests/p4-fabric-tna/run_test_05a_service_provision_acl.sh b/src/tests/p4-fabric-tna/run_test_05a_service_provision_acl.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2cf94b1bd09a39e7908d94a1b4f0fb4ab51f0ae9
--- /dev/null
+++ b/src/tests/p4-fabric-tna/run_test_05a_service_provision_acl.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source tfs_runtime_env_vars.sh
+python3 -m pytest --verbose src/tests/p4-fabric-tna/tests-service/test_functional_service_provision_acl.py
diff --git a/src/tests/p4-fabric-tna/run_test_05b_service_deprovision_acl.sh b/src/tests/p4-fabric-tna/run_test_05b_service_deprovision_acl.sh
new file mode 100755
index 0000000000000000000000000000000000000000..681490896f54e1bfe5ebc8cb3f3c9b60ef47ead0
--- /dev/null
+++ b/src/tests/p4-fabric-tna/run_test_05b_service_deprovision_acl.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source tfs_runtime_env_vars.sh
+python3 -m pytest --verbose src/tests/p4-fabric-tna/tests-service/test_functional_service_deprovision_acl.py
diff --git a/src/tests/p4-fabric-tna/tests-service/test_functional_service_deprovision_acl.py b/src/tests/p4-fabric-tna/tests-service/test_functional_service_deprovision_acl.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcecbd2c7ce5fdf266e3524a98b07e4cf5bbbb89
--- /dev/null
+++ b/src/tests/p4-fabric-tna/tests-service/test_functional_service_deprovision_acl.py
@@ -0,0 +1,78 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+from common.proto.context_pb2 import ServiceId, ServiceStatusEnum, ServiceTypeEnum
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.tools.object_factory.Service import json_service_id
+from context.client.ContextClient import ContextClient
+from service.client.ServiceClient import ServiceClient
+from tests.Fixtures import context_client, service_client # pylint: disable=unused-import
+from tests.tools.test_tools_p4 import *
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+def test_service_deletion_acl(
+    context_client : ContextClient, # pylint: disable=redefined-outer-name
+    service_client : ServiceClient  # pylint: disable=redefined-outer-name
+) -> None:
+    # Get the current number of devices
+    response = context_client.ListDevices(ADMIN_CONTEXT_ID)
+    LOGGER.warning('Devices[{:d}] = {:s}'.format(len(response.devices), grpc_message_to_json_string(response)))
+
+    # Total devices
+    dev_nb = len(response.devices)
+    assert dev_nb == DEV_NB
+
+    # P4 devices
+    p4_dev_nb = identify_number_of_p4_devices(response.devices)
+    assert p4_dev_nb == P4_DEV_NB
+
+    # Get the current number of rules in the P4 devices
+    p4_rules_before_deletion = get_number_of_rules(response.devices)
+
+    # Get the current number of services
+    response = context_client.ListServices(ADMIN_CONTEXT_ID)
+    services_nb_before_deletion = len(response.services)
+    assert verify_active_service_type(response.services, ServiceTypeEnum.SERVICETYPE_ACL)
+
+    for service in response.services:
+        # Ignore services of other types
+        if service.service_type != ServiceTypeEnum.SERVICETYPE_ACL:
+            continue
+
+        service_id = service.service_id
+        assert service_id
+
+        service_uuid = service_id.service_uuid.uuid
+        context_uuid = service_id.context_id.context_uuid.uuid
+        assert service.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE
+
+        # Delete ACL service
+        service_client.DeleteService(ServiceId(**json_service_id(service_uuid, json_context_id(context_uuid))))
+
+    # Get an updated view of the services
+    response = context_client.ListServices(ADMIN_CONTEXT_ID)
+    services_nb_after_deletion = len(response.services)
+    assert services_nb_after_deletion == services_nb_before_deletion - 1, "Exactly one new service must be deleted"
+
+    # Get an updated view of the devices
+    response = context_client.ListDevices(ADMIN_CONTEXT_ID)
+    p4_rules_after_deletion = get_number_of_rules(response.devices)
+
+    rules_diff = p4_rules_before_deletion - p4_rules_after_deletion
+
+    assert p4_rules_after_deletion < p4_rules_before_deletion, "ACL service must contain some rules"
+    assert rules_diff == P4_DEV_NB * ACL_RULES, "ACL service must contain {} rules per device".format(ACL_RULES)
diff --git a/src/tests/p4-fabric-tna/tests-service/test_functional_service_provision_acl.py b/src/tests/p4-fabric-tna/tests-service/test_functional_service_provision_acl.py
new file mode 100644
index 0000000000000000000000000000000000000000..58de046b4171f12ccfc39d078e66cf5ad0670d2a
--- /dev/null
+++ b/src/tests/p4-fabric-tna/tests-service/test_functional_service_provision_acl.py
@@ -0,0 +1,73 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum
+from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from context.client.ContextClient import ContextClient
+from device.client.DeviceClient import DeviceClient
+from service.client.ServiceClient import ServiceClient
+from tests.Fixtures import context_client, device_client, service_client # pylint: disable=unused-import
+from tests.tools.test_tools_p4 import *
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+def test_service_creation_acl(
+    context_client : ContextClient, # pylint: disable=redefined-outer-name
+    device_client  : DeviceClient,  # pylint: disable=redefined-outer-name
+    service_client : ServiceClient  # pylint: disable=redefined-outer-name
+) -> None:
+    # Get the current number of services
+    response = context_client.ListServices(ADMIN_CONTEXT_ID)
+    services_nb_before = len(response.services)
+
+    # Get the current number of devices
+    response = context_client.ListDevices(ADMIN_CONTEXT_ID)
+    LOGGER.warning('Devices[{:d}] = {:s}'.format(len(response.devices), grpc_message_to_json_string(response)))
+
+    # Total devices
+    dev_nb = len(response.devices)
+    assert dev_nb == DEV_NB
+
+    # P4 devices
+    p4_dev_nb = identify_number_of_p4_devices(response.devices)
+    assert p4_dev_nb == P4_DEV_NB
+
+    # Get the current number of rules in the P4 devices
+    p4_rules_before = get_number_of_rules(response.devices)
+
+    # Load service
+    descriptor_loader = DescriptorLoader(
+        descriptors_file=DESC_FILE_SERVICE_CREATE_ACL,
+        context_client=context_client, device_client=device_client, service_client=service_client
+    )
+    results = descriptor_loader.process()
+    check_descriptor_load_results(results, descriptor_loader)
+
+    # Get an updated view of the services
+    response = context_client.ListServices(ADMIN_CONTEXT_ID)
+    services_nb_after = len(response.services)
+    assert services_nb_after == services_nb_before + 1, "Exactly one new service must be in place"
+    assert verify_active_service_type(response.services, ServiceTypeEnum.SERVICETYPE_ACL)
+
+    # Get an updated view of the devices
+    response = context_client.ListDevices(ADMIN_CONTEXT_ID)
+    p4_rules_after = get_number_of_rules(response.devices)
+
+    rules_diff = p4_rules_after - p4_rules_before
+
+    assert p4_rules_after > p4_rules_before, "ACL service must install some rules"
+    assert rules_diff == P4_DEV_NB * ACL_RULES, "ACL service must install {} rules per device".format(ACL_RULES)