feat(core): Core allocation
[csit.git] / resources / libraries / python / IPsecUtil.py
index 2efc70e..39c6a4c 100644 (file)
@@ -1,5 +1,5 @@
-# Copyright (c) 2021 Cisco and/or its affiliates.
-# Copyright (c) 2021 PANTHEON.tech s.r.o.
+# Copyright (c) 2023 Cisco and/or its affiliates.
+# Copyright (c) 2023 PANTHEON.tech s.r.o.
 # 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:
@@ -22,6 +22,8 @@ from ipaddress import ip_network, ip_address
 from random import choice
 from string import ascii_letters
 
+from robot.libraries.BuiltIn import BuiltIn
+
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.IncrementUtil import ObjIncrement
 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
@@ -32,8 +34,8 @@ from resources.libraries.python.IPUtil import IPUtil, IpDscp, \
 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.ssh import scp_node
 from resources.libraries.python.topology import Topology, NodeType
-from resources.libraries.python.VatExecutor import VatExecutor
 from resources.libraries.python.VPPUtil import VPPUtil
+from resources.libraries.python.FlowUtil import FlowUtil
 
 
 IPSEC_UDP_PORT_NONE = 0xffff
@@ -358,25 +360,26 @@ class IPsecUtil:
 
     @staticmethod
     def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
-            nodes, workers, crypto_enable=False):
+            nodes, crypto_enable=False):
         """Enable or disable crypto on specific vpp worker threads.
 
         :param node: VPP node to enable or disable crypto for worker threads.
-        :param workers: List of VPP thread numbers.
         :param crypto_enable: Disable or enable crypto work.
         :type node: dict
-        :type workers: Iterable[int]
         :type crypto_enable: bool
         :raises RuntimeError: If failed to enable or disable crypto for worker
             thread or if no API reply received.
         """
-        for node in nodes.values():
-            if node[u"type"] == NodeType.DUT:
+        for node_name, node in nodes.items():
+            if node["type"] == NodeType.DUT:
                 thread_data = VPPUtil.vpp_show_threads(node)
                 worker_cnt = len(thread_data) - 1
                 if not worker_cnt:
                     return None
                 worker_ids = list()
+                workers = BuiltIn().get_variable_value(
+                    f"${{{node_name}_cpu_dp}}"
+                )
                 for item in thread_data:
                     if str(item.cpu_id) in workers.split(u","):
                         worker_ids.append(item.id)
@@ -437,7 +440,7 @@ class IPsecUtil:
             src_addr = u""
             dst_addr = u""
 
-        cmd = u"ipsec_sad_entry_add_del_v2"
+        cmd = u"ipsec_sad_entry_add"
         err_msg = f"Failed to add Security Association Database entry " \
             f"on host {node[u'host']}"
         sad_entry = dict(
@@ -448,27 +451,28 @@ class IPsecUtil:
             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
             integrity_key=ikey,
             flags=flags,
-            tunnel_src=str(src_addr),
-            tunnel_dst=str(dst_addr),
-            tunnel_flags=int(
-                TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+            tunnel=dict(
+                src=str(src_addr),
+                dst=str(dst_addr),
+                table_id=0,
+                encap_decap_flags=int(
+                    TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+                ),
+                dscp=int(IpDscp.IP_API_DSCP_CS0),
             ),
-            dscp=int(IpDscp.IP_API_DSCP_CS0),
             protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
             udp_src_port=4500,  # default value in api
             udp_dst_port=4500  # default value in api
         )
-        args = dict(
-            is_add=True,
-            entry=sad_entry
-        )
+        args = dict(entry=sad_entry)
         with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
     def vpp_ipsec_add_sad_entries(
             node, n_entries, sad_id, spi, crypto_alg, crypto_key,
-            integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
+            integ_alg=None, integ_key=u"", tunnel_src=None,tunnel_dst=None,
+            tunnel_addr_incr=True):
         """Create multiple Security Association Database entries on VPP node.
 
         :param node: VPP node to add SAD entry on.
@@ -485,6 +489,8 @@ class IPsecUtil:
             specified ESP transport mode is used.
         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
             not specified ESP transport mode is used.
+        :param tunnel_addr_incr: Enable or disable tunnel IP address
+            incremental step.
         :type node: dict
         :type n_entries: int
         :type sad_id: int
@@ -495,6 +501,7 @@ class IPsecUtil:
         :type integ_key: str
         :type tunnel_src: str
         :type tunnel_dst: str
+        :type tunnel_addr_incr: bool
         """
         if isinstance(crypto_key, str):
             crypto_key = crypto_key.encode(encoding=u"utf-8")
@@ -507,32 +514,11 @@ class IPsecUtil:
             src_addr = u""
             dst_addr = u""
 
-        addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
-            else 1 << (32 - 24)
-
-        if int(n_entries) > 10:
-            tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
-
-            with open(tmp_filename, 'w') as tmp_file:
-                for i in range(n_entries):
-                    integ = f"integ-alg {integ_alg.alg_name} " \
-                        f"integ-key {integ_key.hex()}" \
-                        if integ_alg else u""
-                    tunnel = f"tunnel src {src_addr + i * addr_incr} " \
-                        f"tunnel dst {dst_addr + i * addr_incr}" \
-                        if tunnel_src and tunnel_dst else u""
-                    conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
-                        f"crypto-alg {crypto_alg.alg_name} " \
-                        f"crypto-key {crypto_key.hex()} " \
-                        f"{integ} {tunnel}\n"
-                    tmp_file.write(conf)
-            vat = VatExecutor()
-            vat.execute_script(
-                tmp_filename, node, timeout=300, json_out=False,
-                copy_on_execute=True
-            )
-            os.remove(tmp_filename)
-            return
+        if tunnel_addr_incr:
+            addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
+                else 1 << (32 - 24)
+        else:
+            addr_incr = 0
 
         ckey = dict(
             length=len(crypto_key),
@@ -551,7 +537,7 @@ class IPsecUtil:
                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
                 )
 
-        cmd = u"ipsec_sad_entry_add_del_v2"
+        cmd = u"ipsec_sad_entry_add"
         err_msg = f"Failed to add Security Association Database entry " \
             f"on host {node[u'host']}"
 
@@ -563,28 +549,32 @@ class IPsecUtil:
             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
             integrity_key=ikey,
             flags=flags,
-            tunnel_src=str(src_addr),
-            tunnel_dst=str(dst_addr),
-            tunnel_flags=int(
-                TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+            tunnel=dict(
+                src=str(src_addr),
+                dst=str(dst_addr),
+                table_id=0,
+                encap_decap_flags=int(
+                    TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+                ),
+                dscp=int(IpDscp.IP_API_DSCP_CS0),
             ),
-            dscp=int(IpDscp.IP_API_DSCP_CS0),
             protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
             udp_src_port=4500,  # default value in api
-            udp_dst_port=4500  # default value in api
-        )
-        args = dict(
-            is_add=True,
-            entry=sad_entry
+            udp_dst_port=4500,  # default value in api
         )
-        with PapiSocketExecutor(node) as papi_exec:
+        args = dict(entry=sad_entry)
+        with PapiSocketExecutor(node, is_async=True) as papi_exec:
             for i in range(n_entries):
                 args[u"entry"][u"sad_id"] = int(sad_id) + i
                 args[u"entry"][u"spi"] = int(spi) + i
-                args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
+                args[u"entry"][u"tunnel"][u"src"] = (
+                    str(src_addr + i * addr_incr)
                     if tunnel_src and tunnel_dst else src_addr
-                args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
+                )
+                args[u"entry"][u"tunnel"][u"dst"] = (
+                    str(dst_addr + i * addr_incr)
                     if tunnel_src and tunnel_dst else dst_addr
+                )
                 history = bool(not 1 < i < n_entries - 2)
                 papi_exec.add(cmd, history=history, **args)
             papi_exec.get_replies(err_msg)
@@ -617,32 +607,10 @@ class IPsecUtil:
         tunnel_src = ip_address(tunnel_src)
         tunnel_dst = ip_address(tunnel_dst)
         traffic_addr = ip_address(traffic_addr)
+        tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
         addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
             else 1 << (32 - raddr_range)
 
-        if int(n_tunnels) > 10:
-            tmp_filename = u"/tmp/ipsec_set_ip.script"
-
-            with open(tmp_filename, 'w') as tmp_file:
-                if_name = Topology.get_interface_name(node, interface)
-                for i in range(n_tunnels):
-                    conf = f"exec set interface ip address {if_name} " \
-                        f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
-                        f"exec ip route add {traffic_addr + i}/" \
-                        f"{128 if traffic_addr.version == 6 else 32} " \
-                        f"via {tunnel_dst + i * addr_incr} {if_name}\n"
-                    if dst_mac:
-                        conf = f"{conf}exec set ip neighbor {if_name} " \
-                               f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
-                    tmp_file.write(conf)
-
-            VatExecutor().execute_script(
-                tmp_filename, node, timeout=300, json_out=False,
-                copy_on_execute=True
-            )
-            os.remove(tmp_filename)
-            return
-
         cmd1 = u"sw_interface_add_del_address"
         args1 = dict(
             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
@@ -672,22 +640,31 @@ class IPsecUtil:
             else f"Failed to configure IP addresses and IP routes " \
                  f"on interface {interface} on host {node[u'host']}"
 
-        with PapiSocketExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node, is_async=True) as papi_exec:
             for i in range(n_tunnels):
+                tunnel_dst_addr = tunnel_dst + i * addr_incr
                 args1[u"prefix"] = IPUtil.create_prefix_object(
                     tunnel_src + i * addr_incr, raddr_range
                 )
                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
                     node, traffic_addr + i,
-                    prefix_len=128 if traffic_addr.version == 6 else 32,
-                    interface=interface, gateway=tunnel_dst + i * addr_incr
+                    prefix_len=tunnel_dst_prefix,
+                    interface=interface, gateway=tunnel_dst_addr
                 )
                 history = bool(not 1 < i < n_tunnels - 2)
-                papi_exec.add(cmd1, history=history, **args1).\
-                    add(cmd2, history=history, **args2)
+                papi_exec.add(cmd1, history=history, **args1)
+                papi_exec.add(cmd2, history=history, **args2)
+
+                args2[u"route"] = IPUtil.compose_vpp_route_structure(
+                    node, tunnel_dst_addr,
+                    prefix_len=tunnel_dst_prefix,
+                    interface=interface, gateway=tunnel_dst_addr
+                )
+                papi_exec.add(cmd2, history=history, **args2)
+
                 if dst_mac:
                     args3[u"neighbor"][u"ip_address"] = ip_address(
-                        tunnel_dst + i * addr_incr
+                        tunnel_dst_addr
                     )
                     papi_exec.add(cmd3, history=history, **args3)
             papi_exec.get_replies(err_msg)
@@ -777,11 +754,11 @@ class IPsecUtil:
         :type action: IPsecUtil.PolicyAction
         :type inbound: bool
         :type bidirectional: bool
-        :raises NotImplemented: When the action is PolicyAction.PROTECT.
+        :raises NotImplementedError: When the action is PolicyAction.PROTECT.
         """
 
         if action == PolicyAction.PROTECT:
-            raise NotImplemented('Policy action PROTECT is not supported.')
+            raise NotImplementedError('Policy action PROTECT is not supported.')
 
         spd_id_dir1 = 1
         spd_id_dir2 = 2
@@ -853,13 +830,17 @@ class IPsecUtil:
         IPsecUtil.vpp_ipsec_show_all(node)
 
     @staticmethod
-    def vpp_ipsec_add_spd_entry(
-            node, spd_id, priority, action, inbound=True, sa_id=None,
+    def _vpp_ipsec_add_spd_entry_internal(
+            executor, spd_id, priority, action, inbound=True, sa_id=None,
             proto=None, laddr_range=None, raddr_range=None, lport_range=None,
             rport_range=None, is_ipv6=False):
-        """Create Security Policy Database entry on the VPP node.
+        """Prepare to create Security Policy Database entry on the VPP node.
 
-        :param node: VPP node to add SPD entry on.
+        This just adds one more command to the executor.
+        The call site shall get replies once all entries are added,
+        to get speed benefit from async PAPI.
+
+        :param executor: Open PAPI executor (async handling) to add commands to.
         :param spd_id: SPD ID to add entry on.
         :param priority: SPD entry priority, higher number = higher priority.
         :param action: Policy action.
@@ -879,7 +860,7 @@ class IPsecUtil:
             <port_start>-<port_end>.
         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
             not defined so it will default to address ::/0, otherwise False.
-        :type node: dict
+        :type executor: PapiSocketExecutor
         :type spd_id: int
         :type priority: int
         :type action: IPsecUtil.PolicyAction
@@ -901,9 +882,7 @@ class IPsecUtil:
         local_net = ip_network(laddr_range, strict=False)
         remote_net = ip_network(raddr_range, strict=False)
 
-        cmd = u"ipsec_spd_entry_add_del"
-        err_msg = f"Failed to add entry to Security Policy Database " \
-                  f"{spd_id} on host {node[u'host']}"
+        cmd = u"ipsec_spd_entry_add_del_v2"
 
         spd_entry = dict(
             spd_id=int(spd_id),
@@ -911,7 +890,7 @@ class IPsecUtil:
             is_outbound=not inbound,
             sa_id=int(sa_id) if sa_id else 0,
             policy=int(action),
-            protocol=int(proto) if proto else 0,
+            protocol=255 if proto is None else int(proto),
             remote_address_start=IPAddress.create_ip_address_object(
                 remote_net.network_address
             ),
@@ -937,8 +916,56 @@ class IPsecUtil:
             is_add=True,
             entry=spd_entry
         )
-        with PapiSocketExecutor(node) as papi_exec:
-            papi_exec.add(cmd, **args).get_reply(err_msg)
+        executor.add(cmd, **args)
+
+    @staticmethod
+    def vpp_ipsec_add_spd_entry(
+            node, spd_id, priority, action, inbound=True, sa_id=None,
+            proto=None, laddr_range=None, raddr_range=None, lport_range=None,
+            rport_range=None, is_ipv6=False):
+        """Create Security Policy Database entry on the VPP node.
+
+        :param node: VPP node to add SPD entry on.
+        :param spd_id: SPD ID to add entry on.
+        :param priority: SPD entry priority, higher number = higher priority.
+        :param action: Policy action.
+        :param inbound: If True policy is for inbound traffic, otherwise
+            outbound.
+        :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
+        :param proto: Policy selector next layer protocol number.
+        :param laddr_range: Policy selector local IPv4 or IPv6 address range
+            in format IP/prefix or IP/mask. If no mask is provided,
+            it's considered to be /32.
+        :param raddr_range: Policy selector remote IPv4 or IPv6 address range
+            in format IP/prefix or IP/mask. If no mask is provided,
+            it's considered to be /32.
+        :param lport_range: Policy selector local TCP/UDP port range in format
+            <port_start>-<port_end>.
+        :param rport_range: Policy selector remote TCP/UDP port range in format
+            <port_start>-<port_end>.
+        :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
+            not defined so it will default to address ::/0, otherwise False.
+        :type node: dict
+        :type spd_id: int
+        :type priority: int
+        :type action: IPsecUtil.PolicyAction
+        :type inbound: bool
+        :type sa_id: int
+        :type proto: int
+        :type laddr_range: string
+        :type raddr_range: string
+        :type lport_range: string
+        :type rport_range: string
+        :type is_ipv6: bool
+        """
+        err_msg = f"Failed to add entry to Security Policy Database " \
+                  f"{spd_id} on host {node[u'host']}"
+        with PapiSocketExecutor(node, is_async=True) as papi_exec:
+            IPsecUtil._vpp_ipsec_add_spd_entry_internal(
+                papi_exec, spd_id, priority, action, inbound, sa_id, proto,
+                laddr_range, raddr_range, lport_range, rport_range, is_ipv6
+            )
+            papi_exec.get_replies(err_msg)
 
     @staticmethod
     def vpp_ipsec_add_spd_entries(
@@ -1000,266 +1027,17 @@ class IPsecUtil:
         if rport_range:
             rport_range_start, rport_range_stop = rport_range.split('-')
 
-        if int(n_entries) > 10:
-            tmp_filename = f"/tmp/ipsec_spd_{spd_id}_add_del_entry.script"
-
-            with open(tmp_filename, 'w') as tmp_file:
-                for i in range(n_entries):
-                    direction = u'inbound' if inbound else u'outbound'
-                    sa = f' sa {sa_id.inc_fmt()}' if sa_id is not None else ''
-                    protocol = f' protocol {protocol}' if proto else ''
-                    local_port_range = f' local-port-range ' \
-                        f'{lport_range_start} - {lport_range_stop}' \
-                        if lport_range else ''
-                    remote_port_range = f' remote-port-range ' \
-                        f'{rport_range_start} - {rport_range_stop}' \
-                        if rport_range else ''
-
-                    spd_cfg = f"exec ipsec policy add spd {spd_id} " \
-                        f"priority {priority.inc_fmt()} {direction}" \
-                        f"{protocol} action {action}{sa} " \
-                        f"local-ip-range {laddr_range.inc_fmt()} " \
-                        f"remote-ip-range {raddr_range.inc_fmt()}" \
-                        f"{local_port_range}{remote_port_range}\n"
-
-                    tmp_file.write(spd_cfg)
-
-            VatExecutor().execute_script(
-                tmp_filename, node, timeout=300, json_out=False,
-                copy_on_execute=True
-            )
-            os.remove(tmp_filename)
-            return
-
-        for i in range(n_entries):
-            IPsecUtil.vpp_ipsec_add_spd_entry(
-                node, spd_id, next(priority), action, inbound,
-                next(sa_id) if sa_id is not None else sa_id,
-                proto, next(laddr_range), next(raddr_range), lport_range,
-                rport_range, is_ipv6
-            )
-
-    @staticmethod
-    def _ipsec_create_tunnel_interfaces_dut1_vat(
-            nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
-            raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
-        """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
-
-        Generate random keys and return them (so DUT2 or TG can decrypt).
-
-        :param nodes: VPP nodes to create tunnel interfaces.
-        :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
-            IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
-            IPv4/IPv6 address (ip2).
-        :param if1_key: VPP node 1 interface key from topology file.
-        :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
-            interface key from topology file.
-        :param n_tunnels: Number of tunnel interfaces to be there at the end.
-        :param crypto_alg: The encryption algorithm name.
-        :param integ_alg: The integrity algorithm name.
-        :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
-            first tunnel in direction node2->node1.
-        :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
-        :param addr_incr: IP / IPv6 address incremental step.
-        :param existing_tunnels: Number of tunnel interfaces before creation.
-            Useful mainly for reconf tests. Default 0.
-        :type nodes: dict
-        :type tun_ips: dict
-        :type if1_key: str
-        :type if2_key: str
-        :type n_tunnels: int
-        :type crypto_alg: CryptoAlg
-        :type integ_alg: Optional[IntegAlg]
-        :type raddr_ip2: IPv4Address or IPv6Address
-        :type addr_incr: int
-        :type spi_d: dict
-        :type existing_tunnels: int
-        :returns: Generated ckeys and ikeys.
-        :rtype: List[bytes], List[bytes]
-        """
-        tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
-        if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
-
-        ckeys = [bytes()] * existing_tunnels
-        ikeys = [bytes()] * existing_tunnels
-
-        vat = VatExecutor()
-        with open(tmp_fn1, u"w") as tmp_f1:
-            rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
-                if u"DUT2" in nodes.keys() \
-                else Topology.get_interface_mac(nodes[u"TG"], if2_key)
-            if not existing_tunnels:
-                tmp_f1.write(
-                    f"exec create loopback interface\n"
-                    f"exec set interface state loop0 up\n"
-                    f"exec set interface ip address {if1_n} "
-                    f"{tun_ips[u'ip2'] - 1}/"
-                    f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
-                    f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
-                    f"static\n"
-                )
-            for i in range(existing_tunnels, n_tunnels):
-                ckeys.append(
-                    gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
-                )
-                ikeys.append(
-                    gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
-                )
-                if integ_alg:
-                    integ = f"integ-alg {integ_alg.alg_name} " \
-                        f"integ-key {ikeys[i].hex()} "
-                else:
-                    integ = u""
-                tmp_f1.write(
-                    f"exec set interface ip address loop0 "
-                    f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
-                    f"exec create ipip tunnel "
-                    f"src {tun_ips[u'ip1'] + i * addr_incr} "
-                    f"dst {tun_ips[u'ip2']} "
-                    f"p2p\n"
-                    f"exec ipsec sa add {i} "
-                    f"spi {spi_d[u'spi_1'] + i} "
-                    f"crypto-alg {crypto_alg.alg_name} "
-                    f"crypto-key {ckeys[i].hex()} "
-                    f"{integ}"
-                    f"esp\n"
-                    f"exec ipsec sa add {100000 + i} "
-                    f"spi {spi_d[u'spi_2'] + i} "
-                    f"crypto-alg {crypto_alg.alg_name} "
-                    f"crypto-key {ckeys[i].hex()} "
-                    f"{integ}"
-                    f"esp\n"
-                    f"exec ipsec tunnel protect ipip{i} "
-                    f"sa-out {i} "
-                    f"sa-in {100000 + i} "
-                    f"add\n"
-                )
-        vat.execute_script(
-            tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
-            copy_on_execute=True,
-            history=bool(n_tunnels < 100)
-        )
-        os.remove(tmp_fn1)
-
-        with open(tmp_fn1, 'w') as tmp_f1:
-            for i in range(existing_tunnels, n_tunnels):
-                tmp_f1.write(
-                    f"exec set interface unnumbered ipip{i} use {if1_n}\n"
-                    f"exec set interface state ipip{i} up\n"
-                    f"exec ip route add "
-                    f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
-                    f"via ipip{i}\n"
-                )
-        vat.execute_script(
-            tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
-            copy_on_execute=True,
-            history=bool(n_tunnels < 100)
-        )
-        os.remove(tmp_fn1)
-
-        return ckeys, ikeys
-
-    @staticmethod
-    def _ipsec_create_tunnel_interfaces_dut2_vat(
-            nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
-            ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
-        """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
-
-        This method accesses keys generated by DUT1 method
-        and does not return anything.
-
-        :param nodes: VPP nodes to create tunnel interfaces.
-        :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
-            IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
-            IPv4/IPv6 address (ip2).
-        :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
-            interface key from topology file.
-        :param n_tunnels: Number of tunnel interfaces to be there at the end.
-        :param crypto_alg: The encryption algorithm name.
-        :param ckeys: List of encryption keys.
-        :param integ_alg: The integrity algorithm name.
-        :param ikeys: List of integrity keys.
-        :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
-        :param addr_incr: IP / IPv6 address incremental step.
-        :param existing_tunnels: Number of tunnel interfaces before creation.
-            Useful mainly for reconf tests. Default 0.
-        :type nodes: dict
-        :type tun_ips: dict
-        :type if2_key: str
-        :type n_tunnels: int
-        :type crypto_alg: CryptoAlg
-        :type ckeys: Sequence[bytes]
-        :type integ_alg: Optional[IntegAlg]
-        :type ikeys: Sequence[bytes]
-        :type addr_incr: int
-        :type spi_d: dict
-        :type existing_tunnels: int
-        """
-        tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
-        if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
-
-        vat = VatExecutor()
-        with open(tmp_fn2, 'w') as tmp_f2:
-            if not existing_tunnels:
-                tmp_f2.write(
-                    f"exec set interface ip address {if2_n}"
-                    f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
-                )
-            for i in range(existing_tunnels, n_tunnels):
-                if integ_alg:
-                    integ = f"integ-alg {integ_alg.alg_name} " \
-                        f"integ-key {ikeys[i].hex()} "
-                else:
-                    integ = u""
-                tmp_f2.write(
-                    f"exec create ipip tunnel "
-                    f"src {tun_ips[u'ip2']} "
-                    f"dst {tun_ips[u'ip1'] + i * addr_incr} "
-                    f"p2p\n"
-                    f"exec ipsec sa add {100000 + i} "
-                    f"spi {spi_d[u'spi_2'] + i} "
-                    f"crypto-alg {crypto_alg.alg_name} "
-                    f"crypto-key {ckeys[i].hex()} "
-                    f"{integ}"
-                    f"esp\n"
-                    f"exec ipsec sa add {i} "
-                    f"spi {spi_d[u'spi_1'] + i} "
-                    f"crypto-alg {crypto_alg.alg_name} "
-                    f"crypto-key {ckeys[i].hex()} "
-                    f"{integ}"
-                    f"esp\n"
-                    f"exec ipsec tunnel protect ipip{i} "
-                    f"sa-out {100000 + i} "
-                    f"sa-in {i} "
-                    f"add\n"
-                )
-        vat.execute_script(
-            tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
-            copy_on_execute=True,
-            history=bool(n_tunnels < 100)
-        )
-        os.remove(tmp_fn2)
-
-        with open(tmp_fn2, 'w') as tmp_f2:
-            if not existing_tunnels:
-                tmp_f2.write(
-                    f"exec ip route add {tun_ips[u'ip1']}/8 "
-                    f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
-                )
-            for i in range(existing_tunnels, n_tunnels):
-                tmp_f2.write(
-                    f"exec set interface unnumbered ipip{i} use {if2_n}\n"
-                    f"exec set interface state ipip{i} up\n"
-                    f"exec ip route add "
-                    f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
-                    f"via ipip{i}\n"
+        err_msg = f"Failed to add entry to Security Policy Database " \
+                  f"{spd_id} on host {node[u'host']}"
+        with PapiSocketExecutor(node, is_async=True) as papi_exec:
+            for _ in range(n_entries):
+                IPsecUtil._vpp_ipsec_add_spd_entry_internal(
+                    papi_exec, spd_id, next(priority), action, inbound,
+                    next(sa_id) if sa_id is not None else sa_id,
+                    proto, next(laddr_range), next(raddr_range), lport_range,
+                    rport_range, is_ipv6
                 )
-        vat.execute_script(
-            tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
-            copy_on_execute=True,
-            history=bool(n_tunnels < 100)
-        )
-        os.remove(tmp_fn2)
+            papi_exec.get_replies(err_msg)
 
     @staticmethod
     def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
@@ -1288,8 +1066,8 @@ class IPsecUtil:
             )
             err_msg = f"Failed to create loopback interface " \
                 f"on host {nodes[u'DUT1'][u'host']}"
-            loop_sw_if_idx = papi_exec.add(cmd, **args). \
-                get_sw_if_index(err_msg)
+            papi_exec.add(cmd, **args)
+            loop_sw_if_idx = papi_exec.get_sw_if_index(err_msg)
             cmd = u"sw_interface_set_flags"
             args = dict(
                 sw_if_index=loop_sw_if_idx,
@@ -1383,7 +1161,7 @@ class IPsecUtil:
             loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
                 nodes[u"DUT1"], u"loop0"
             )
-        with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
+        with PapiSocketExecutor(nodes[u"DUT1"], is_async=True) as papi_exec:
             # Configure IP addresses on loop0 interface
             cmd = u"sw_interface_add_del_address"
             args = dict(
@@ -1439,7 +1217,7 @@ class IPsecUtil:
             # Configure IPSec SAD entries
             ckeys = [bytes()] * existing_tunnels
             ikeys = [bytes()] * existing_tunnels
-            cmd = u"ipsec_sad_entry_add_del_v2"
+            cmd = u"ipsec_sad_entry_add"
             c_key = dict(
                 length=0,
                 data=None
@@ -1457,21 +1235,20 @@ class IPsecUtil:
                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
                 integrity_key=i_key,
                 flags=None,
-                tunnel_src=0,
-                tunnel_dst=0,
-                tunnel_flags=int(
-                    TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+                tunnel=dict(
+                    src=0,
+                    dst=0,
+                    table_id=0,
+                    encap_decap_flags=int(
+                        TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+                    ),
+                    dscp=int(IpDscp.IP_API_DSCP_CS0),
                 ),
-                dscp=int(IpDscp.IP_API_DSCP_CS0),
-                table_id=0,
                 salt=0,
                 udp_src_port=IPSEC_UDP_PORT_NONE,
-                udp_dst_port=IPSEC_UDP_PORT_NONE
-            )
-            args = dict(
-                is_add=True,
-                entry=sad_entry
+                udp_dst_port=IPSEC_UDP_PORT_NONE,
             )
+            args = dict(entry=sad_entry)
             for i in range(existing_tunnels, n_tunnels):
                 ckeys.append(
                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
@@ -1624,7 +1401,7 @@ class IPsecUtil:
         :type spi_d: dict
         :type existing_tunnels: int
         """
-        with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
+        with PapiSocketExecutor(nodes[u"DUT2"], is_async=True) as papi_exec:
             if not existing_tunnels:
                 # Set IP address on VPP node 2 interface
                 cmd = u"sw_interface_add_del_address"
@@ -1641,7 +1418,7 @@ class IPsecUtil:
                 )
                 err_msg = f"Failed to set IP address on interface {if2_key} " \
                     f"on host {nodes[u'DUT2'][u'host']}"
-                papi_exec.add(cmd, **args).get_reply(err_msg)
+                papi_exec.add(cmd, **args).get_replies(err_msg)
             # Configure IPIP tunnel interfaces
             cmd = u"ipip_add_tunnel"
             ipip_tunnel = dict(
@@ -1679,7 +1456,7 @@ class IPsecUtil:
                 ]
             )
             # Configure IPSec SAD entries
-            cmd = u"ipsec_sad_entry_add_del_v2"
+            cmd = u"ipsec_sad_entry_add"
             c_key = dict(
                 length=0,
                 data=None
@@ -1692,28 +1469,25 @@ class IPsecUtil:
                 sad_id=None,
                 spi=None,
                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
-
                 crypto_algorithm=crypto_alg.alg_int_repr,
                 crypto_key=c_key,
                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
                 integrity_key=i_key,
-
                 flags=None,
-                tunnel_src=0,
-                tunnel_dst=0,
-                tunnel_flags=int(
-                    TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+                tunnel=dict(
+                    src=0,
+                    dst=0,
+                    table_id=0,
+                    encap_decap_flags=int(
+                        TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+                    ),
+                    dscp=int(IpDscp.IP_API_DSCP_CS0),
                 ),
-                dscp=int(IpDscp.IP_API_DSCP_CS0),
-                table_id=0,
                 salt=0,
                 udp_src_port=IPSEC_UDP_PORT_NONE,
-                udp_dst_port=IPSEC_UDP_PORT_NONE
-            )
-            args = dict(
-                is_add=True,
-                entry=sad_entry
+                udp_dst_port=IPSEC_UDP_PORT_NONE,
             )
+            args = dict(entry=sad_entry)
             for i in range(existing_tunnels, n_tunnels):
                 ckeys.append(
                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
@@ -1906,28 +1680,16 @@ class IPsecUtil:
         addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
             else 1 << (32 - raddr_range)
 
-        if n_tunnels - existing_tunnels > 10:
-            ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
-                nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
-                integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
-            )
-            if u"DUT2" in nodes.keys():
-                IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
-                    nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
-                    integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
-                    existing_tunnels
-                )
-        else:
-            ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
-                nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
-                integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
+        ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
+            nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
+            integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
+        )
+        if u"DUT2" in nodes.keys():
+            IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
+                nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
+                integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
+                existing_tunnels
             )
-            if u"DUT2" in nodes.keys():
-                IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
-                    nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
-                    integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
-                    existing_tunnels
-                )
 
         if return_keys:
             return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
@@ -2088,7 +1850,8 @@ class IPsecUtil:
     @staticmethod
     def vpp_ipsec_add_multiple_tunnels(
             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
-            tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
+            tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range,
+            tunnel_addr_incr=True):
         """Create multiple IPsec tunnels between two VPP nodes.
 
         :param nodes: VPP nodes to create tunnels.
@@ -2105,6 +1868,8 @@ class IPsecUtil:
             first tunnel in direction node2->node1.
         :param raddr_range: Mask specifying range of Policy selector Remote
             IPv4 addresses. Valid values are from 1 to 32.
+        :param tunnel_addr_incr: Enable or disable tunnel IP address
+            incremental step.
         :type nodes: dict
         :type interface1: str or int
         :type interface2: str or int
@@ -2116,6 +1881,7 @@ class IPsecUtil:
         :type raddr_ip1: string
         :type raddr_ip2: string
         :type raddr_range: int
+        :type tunnel_addr_incr: bool
         """
         spd_id = 1
         p_hi = 100
@@ -2141,18 +1907,31 @@ class IPsecUtil:
 
         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
-        IPsecUtil.vpp_ipsec_add_spd_entry(
-            nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
-            proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
-        )
-        IPsecUtil.vpp_ipsec_add_spd_entry(
-            nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
-            proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
-        )
+
+        addr_incr = 1 << (128 - 96) if ip_address(tunnel_ip1).version == 6 \
+            else 1 << (32 - 24)
+        for i in range(n_tunnels//(addr_incr**2)+1):
+            dut1_local_outbound_range = \
+                ip_network(f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8",
+                False).with_prefixlen
+            dut1_remote_outbound_range = \
+                ip_network(f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8",
+                False).with_prefixlen
+
+            IPsecUtil.vpp_ipsec_add_spd_entry(
+                nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
+                proto=50, laddr_range=dut1_local_outbound_range,
+                raddr_range=dut1_remote_outbound_range
+            )
+            IPsecUtil.vpp_ipsec_add_spd_entry(
+                nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
+                proto=50, laddr_range=dut1_remote_outbound_range,
+                raddr_range=dut1_local_outbound_range
+            )
 
         IPsecUtil.vpp_ipsec_add_sad_entries(
             nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
-            integ_alg, integ_key, tunnel_ip1, tunnel_ip2
+            integ_alg, integ_key, tunnel_ip1, tunnel_ip2, tunnel_addr_incr
         )
 
         IPsecUtil.vpp_ipsec_add_spd_entries(
@@ -2164,7 +1943,7 @@ class IPsecUtil:
 
         IPsecUtil.vpp_ipsec_add_sad_entries(
             nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
-            integ_alg, integ_key, tunnel_ip2, tunnel_ip1
+            integ_alg, integ_key, tunnel_ip2, tunnel_ip1, tunnel_addr_incr
         )
         IPsecUtil.vpp_ipsec_add_spd_entries(
             nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
@@ -2174,29 +1953,40 @@ class IPsecUtil:
         )
 
         if u"DUT2" in nodes.keys():
+            rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
             IPsecUtil.vpp_ipsec_set_ip_route(
                 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
-                interface2, raddr_range)
+                interface2, raddr_range, rmac)
 
             IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
             IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
-            IPsecUtil.vpp_ipsec_add_spd_entry(
-                nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
-                inbound=False, proto=50, laddr_range=u"100.0.0.0/8",
-                raddr_range=u"100.0.0.0/8"
-            )
-            IPsecUtil.vpp_ipsec_add_spd_entry(
-                nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
-                inbound=True, proto=50, laddr_range=u"100.0.0.0/8",
-                raddr_range=u"100.0.0.0/8"
-            )
+            for i in range(n_tunnels//(addr_incr**2)+1):
+                dut2_local_outbound_range = \
+                    ip_network(f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8",
+                    False).with_prefixlen
+                dut2_remote_outbound_range = \
+                    ip_network(f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8",
+                    False).with_prefixlen
+
+                IPsecUtil.vpp_ipsec_add_spd_entry(
+                    nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
+                    inbound=False, proto=50, laddr_range=dut2_remote_outbound_range,
+                    raddr_range=dut2_local_outbound_range
+                )
+                IPsecUtil.vpp_ipsec_add_spd_entry(
+                    nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
+                    inbound=True, proto=50, laddr_range=dut2_local_outbound_range,
+                    raddr_range=dut2_remote_outbound_range
+                )
 
             IPsecUtil.vpp_ipsec_add_sad_entries(
                 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
-                crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2
+                crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2,
+                tunnel_addr_incr
             )
             IPsecUtil.vpp_ipsec_add_spd_entries(
-                nodes[u"DUT2"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
+                nodes[u"DUT2"], n_tunnels, spd_id,
+                priority=ObjIncrement(p_lo, 0),
                 action=PolicyAction.PROTECT, inbound=True,
                 sa_id=ObjIncrement(sa_id_1, 1),
                 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
@@ -2204,10 +1994,12 @@ class IPsecUtil:
 
             IPsecUtil.vpp_ipsec_add_sad_entries(
                 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
-                crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1
+                crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1,
+                tunnel_addr_incr
             )
             IPsecUtil.vpp_ipsec_add_spd_entries(
-                nodes[u"DUT2"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
+                nodes[u"DUT2"], n_tunnels, spd_id,
+                priority=ObjIncrement(p_lo, 0),
                 action=PolicyAction.PROTECT, inbound=False,
                 sa_id=ObjIncrement(sa_id_2, 1),
                 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
@@ -2230,6 +2022,56 @@ class IPsecUtil:
         :type node: dict
         """
         cmds = [
-            u"ipsec_sa_v2_dump"
+            u"ipsec_sa_v3_dump"
         ]
         PapiSocketExecutor.dump_and_log(node, cmds)
+
+    @staticmethod
+    def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
+        """Ipsec flow enable rss action.
+
+        :param node: DUT node.
+        :param proto: The flow protocol.
+        :param type: RSS type.
+        :param function: RSS function.
+
+        :type node: dict
+        :type proto: str
+        :type type: str
+        :type function: str
+        :returns: flow_index.
+        """
+        # TODO: to be fixed to use full PAPI when it is ready in VPP
+        cmd = f"test flow add src-ip any proto {proto} rss function " \
+            f"{function} rss types {type}"
+        stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
+        flow_index = stdout.split()[1]
+
+        return flow_index
+
+    @staticmethod
+    def vpp_create_ipsec_flows_on_dut(
+            node, n_flows, rx_queues, spi_start, interface):
+        """Create mutiple ipsec flows and enable flows onto interface.
+
+        :param node: DUT node.
+        :param n_flows: Number of flows to create.
+        :param rx_queues: NUmber of RX queues.
+        :param spi_start: The start spi.
+        :param interface: Name of the interface.
+
+        :type node: dict
+        :type n_flows: int
+        :type rx_queues: int
+        :type spi_start: int
+        :type interface: str
+        :returns: flow_index.
+        """
+
+        for i in range(0, n_flows):
+            rx_queue = i%rx_queues
+
+            spi = spi_start + i
+            flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
+                    node, "ESP", spi, "redirect-to-queue", value=rx_queue)
+            FlowUtil.vpp_flow_enable(node, interface, flow_index)