aboutsummaryrefslogtreecommitdiffstats
path: root/resources
diff options
context:
space:
mode:
Diffstat (limited to 'resources')
-rw-r--r--resources/libraries/python/DUTSetup.py184
-rw-r--r--resources/libraries/python/IPsecUtil.py331
-rw-r--r--resources/libraries/python/TrafficGenerator.py46
-rw-r--r--resources/libraries/python/VatExecutor.py73
-rw-r--r--resources/libraries/python/VppConfigGenerator.py62
-rw-r--r--resources/libraries/python/topology.py29
-rw-r--r--resources/libraries/robot/default.robot54
-rw-r--r--resources/libraries/robot/ipsec.robot2
-rw-r--r--resources/libraries/robot/performance.robot51
-rw-r--r--resources/templates/vat/show_crypto_device_mapping.vat1
10 files changed, 815 insertions, 18 deletions
diff --git a/resources/libraries/python/DUTSetup.py b/resources/libraries/python/DUTSetup.py
index 275a546fd8..dabdfceef2 100644
--- a/resources/libraries/python/DUTSetup.py
+++ b/resources/libraries/python/DUTSetup.py
@@ -16,6 +16,7 @@
from robot.api import logger
from resources.libraries.python.topology import NodeType
+from resources.libraries.python.topology import Topology
from resources.libraries.python.ssh import SSH
from resources.libraries.python.constants import Constants
from resources.libraries.python.VatExecutor import VatExecutor
@@ -147,3 +148,186 @@ class DUTSetup(object):
if node['type'] == NodeType.DUT:
pids[node['host']] = DUTSetup.get_vpp_pid(node)
return pids
+
+ @staticmethod
+ def vpp_show_crypto_device_mapping(node):
+ """Run "show crypto device mapping" CLI command.
+
+ :param node: Node to run command on.
+ :type node: dict
+ """
+ vat = VatExecutor()
+ vat.execute_script("show_crypto_device_mapping.vat", node,
+ json_out=False)
+
+ @staticmethod
+ def crypto_device_verify(node, force_init=False, numvfs=32):
+ """Verify if Crypto QAT device virtual functions are initialized on all
+ DUTs. If parameter force initialization is set to True, then try to
+ initialize or disable QAT.
+
+ :param node: DUT node.
+ :param force_init: If True then try to initialize to specific value.
+ :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
+ :type node: dict
+ :type force_init: bool
+ :type numvfs: int
+ :returns: nothing
+ :raises RuntimeError: If QAT is not initialized or failed to initialize.
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ cryptodev = Topology.get_cryptodev(node)
+ cmd = 'cat /sys/bus/pci/devices/{}/sriov_numvfs'.format(
+ cryptodev.replace(':', r'\:'))
+
+ # Try to read number of VFs from PCI address of QAT device
+ for _ in range(3):
+ ret_code, stdout, _ = ssh.exec_command(cmd)
+ if int(ret_code) == 0:
+ try:
+ sriov_numvfs = int(stdout)
+ except ValueError:
+ logger.trace('Reading sriov_numvfs info failed on: {}'\
+ .format(node['host']))
+ else:
+ if sriov_numvfs != numvfs:
+ if force_init:
+ # QAT is not initialized and we want to initialize
+ # with numvfs
+ DUTSetup.crypto_device_init(node, numvfs)
+ else:
+ raise RuntimeError('QAT device {} is not '\
+ 'initialized to {} on host: {}'.format(\
+ cryptodev, numvfs, node['host']))
+ break
+
+ @staticmethod
+ def crypto_device_init(node, numvfs):
+ """Init Crypto QAT device virtual functions on DUT.
+
+ :param node: DUT node.
+ :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
+ :type node: dict
+ :type numvfs: int
+ :returns: nothing
+ :raises RuntimeError: If QAT failed to initialize.
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ cryptodev = Topology.get_cryptodev(node)
+
+ # QAT device must be bind to kernel driver before initialization
+ DUTSetup.pci_driver_unbind(node, cryptodev)
+ DUTSetup.pci_driver_bind(node, cryptodev, "dh895xcc")
+
+ # Initialize QAT VFs
+ ret_code, _, _ = ssh.exec_command(
+ "sudo sh -c 'echo {} | tee /sys/bus/pci/devices/{}/sriov_numvfs'"
+ .format(numvfs, cryptodev.replace(':', r'\:')))
+
+ if int(ret_code) != 0:
+ raise RuntimeError('Failed to initialize {} VFs on QAT device on '
+ 'host: {}'.format(numvfs, node['host']))
+
+ @staticmethod
+ def pci_driver_unbind(node, pci_addr):
+ """Unbind PCI device from current driver on node.
+
+ :param node: DUT node.
+ :param pci_addr: PCI device address.
+ :type node: dict
+ :type pci_addr: str
+ :returns: nothing
+ :raises RuntimeError: If PCI device unbind failed.
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ ret_code, _, _ = ssh.exec_command(
+ "sudo sh -c 'echo {} | tee /sys/bus/pci/devices/{}/driver/unbind'"
+ .format(pci_addr, pci_addr.replace(':', r'\:')))
+
+ if int(ret_code) != 0:
+ raise RuntimeError('Failed to unbind PCI device from driver on '
+ 'host: {}'.format(node['host']))
+
+ @staticmethod
+ def pci_driver_bind(node, pci_addr, driver):
+ """Bind PCI device to driver on node.
+
+ :param node: DUT node.
+ :param pci_addr: PCI device address.
+ :param driver: Driver to bind.
+ :type node: dict
+ :type pci_addr: str
+ :type driver: str
+ :returns: nothing
+ :raises RuntimeError: If PCI device bind failed.
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ ret_code, _, _ = ssh.exec_command(
+ "sudo sh -c 'echo {} | tee /sys/bus/pci/drivers/{}/bind'"
+ .format(pci_addr, driver))
+
+ if int(ret_code) != 0:
+ raise RuntimeError('Failed to bind PCI device to {} driver on '
+ 'host: {}'.format(driver, node['host']))
+
+ @staticmethod
+ def kernel_module_verify(node, module, force_load=False):
+ """Verify if kernel module is loaded on all DUTs. If parameter force
+ load is set to True, then try to load the modules.
+
+ :param node: DUT node.
+ :param module: Module to verify.
+ :param force_load: If True then try to load module.
+ :type node: dict
+ :type module: str
+ :type force_init: bool
+ :returns: nothing
+ :raises RuntimeError: If module is not loaded or failed to load.
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ cmd = 'grep -w {} /proc/modules'.format(module)
+ ret_code, _, _ = ssh.exec_command(cmd)
+
+ if int(ret_code) != 0:
+ if force_load:
+ # Module is not loaded and we want to load it
+ DUTSetup.kernel_module_load(node, module)
+ else:
+ raise RuntimeError('Kernel module {} is not loaded on host: '\
+ '{}'.format(module, node['host']))
+
+ @staticmethod
+ def kernel_module_load(node, module):
+ """Load kernel module on node.
+
+ :param node: DUT node.
+ :param module: Module to load.
+ :type node: dict
+ :type module: str
+ :returns: nothing
+ :raises RuntimeError: If loading failed.
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ ret_code, _, _ = ssh.exec_command_sudo("modprobe {}".format(module))
+
+ if int(ret_code) != 0:
+ raise RuntimeError('Failed to load {} kernel module on host: '\
+ '{}'.format(module, node['host']))
diff --git a/resources/libraries/python/IPsecUtil.py b/resources/libraries/python/IPsecUtil.py
index 5a4a181fd0..a554a54bb4 100644
--- a/resources/libraries/python/IPsecUtil.py
+++ b/resources/libraries/python/IPsecUtil.py
@@ -13,7 +13,8 @@
"""IPsec utilities library."""
-from ipaddress import ip_network
+import os
+from ipaddress import ip_network, ip_address
from enum import Enum
@@ -37,6 +38,7 @@ class CryptoAlg(Enum):
AES_CBC_128 = ('aes-cbc-128', 'AES-CBC', 16)
AES_CBC_192 = ('aes-cbc-192', 'AES-CBC', 24)
AES_CBC_256 = ('aes-cbc-256', 'AES-CBC', 32)
+ AES_GCM_128 = ('aes-gcm-128', 'AES-GCM', 20)
def __init__(self, alg_name, scapy_name, key_len):
self.alg_name = alg_name
@@ -50,6 +52,7 @@ class IntegAlg(Enum):
SHA_256_128 = ('sha-256-128', 'SHA2-256-128', 32)
SHA_384_192 = ('sha-384-192', 'SHA2-384-192', 48)
SHA_512_256 = ('sha-512-256', 'SHA2-512-256', 64)
+ AES_GCM_128 = ('aes-gcm-128', 'AES-GCM', 20)
def __init__(self, alg_name, scapy_name, key_len):
self.alg_name = alg_name
@@ -115,6 +118,15 @@ class IPsecUtil(object):
return CryptoAlg.AES_CBC_256
@staticmethod
+ def crypto_alg_aes_gcm_128():
+ """Return encryption algorithm aes-gcm-128.
+
+ :returns: CryptoAlg enum AES_GCM_128 object.
+ :rtype: CryptoAlg
+ """
+ return CryptoAlg.AES_GCM_128
+
+ @staticmethod
def get_crypto_alg_key_len(crypto_alg):
"""Return encryption algorithm key length.
@@ -173,6 +185,15 @@ class IPsecUtil(object):
return IntegAlg.SHA_512_256
@staticmethod
+ def integ_alg_aes_gcm_128():
+ """Return integrity algorithm AES-GCM-128.
+
+ :returns: IntegAlg enum AES_GCM_128 object.
+ :rtype: IntegAlg
+ """
+ return IntegAlg.AES_GCM_128
+
+ @staticmethod
def get_integ_alg_key_len(integ_alg):
"""Return integrity algorithm key length.
@@ -208,9 +229,9 @@ class IPsecUtil(object):
:param integ_alg: The integrity algorithm name.
:param integ_key: The integrity key string.
:param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
- specified ESP transport mode is used.
+ specified ESP transport mode is used.
:param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
- not specified ESP transport mode is used.
+ not specified ESP transport mode is used.
:type node: dict
:type sad_id: int
:type spi: int
@@ -237,6 +258,57 @@ class IPsecUtil(object):
err_msg='Add SAD entry failed on {0}'.format(node['host']))
@staticmethod
+ def vpp_ipsec_add_sad_entries(node, n_entries, sad_id, spi, crypto_alg,
+ crypto_key, integ_alg, integ_key,
+ tunnel_src=None, tunnel_dst=None):
+ """Create multiple Security Association Database entries on VPP node.
+
+ :param node: VPP node to add SAD entry on.
+ :param n_entries: Number of SAD entries to be created.
+ :param sad_id: First SAD entry ID. All subsequent SAD entries will have
+ id incremented by 1.
+ :param spi: Security Parameter Index of first SAD entry. All subsequent
+ SAD entries will have spi incremented by 1.
+ :param crypto_alg: The encryption algorithm name.
+ :param crypto_key: The encryption key string.
+ :param integ_alg: The integrity algorithm name.
+ :param integ_key: The integrity key string.
+ :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
+ specified ESP transport mode is used.
+ :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
+ not specified ESP transport mode is used.
+ :type node: dict
+ :type n_entries: int
+ :type sad_id: int
+ :type spi: int
+ :type crypto_alg: CryptoAlg
+ :type crypto_key: str
+ :type integ_alg: IntegAlg
+ :type integ_key: str
+ :type tunnel_src: str
+ :type tunnel_dst: str
+ """
+ tmp_filename = '/tmp/ipsec_sad_{}_add_del_entry.vat'.format(sad_id)
+ ckey = crypto_key.encode('hex')
+ ikey = integ_key.encode('hex')
+ tunnel = 'tunnel_src {0} tunnel_dst {1}'.format(tunnel_src, tunnel_dst)\
+ if tunnel_src is not None and tunnel_dst is not None else ''
+
+ integ = 'integ_alg {0} integ_key {1}'.format(integ_alg.alg_name, ikey)\
+ if crypto_alg.alg_name != 'aes-gcm-128' else ''
+
+ with open(tmp_filename, 'w') as tmp_file:
+ for i in range(0, n_entries):
+ buf_str = 'ipsec_sad_add_del_entry esp sad_id {0} spi {1} ' \
+ 'crypto_alg {2} crypto_key {3} {4} {5}\n'.format(
+ sad_id+i, spi+i, crypto_alg.alg_name, ckey, integ,
+ tunnel)
+ tmp_file.write(buf_str)
+ vat = VatExecutor()
+ vat.scp_and_execute_script(tmp_filename, node, 300)
+ os.remove(tmp_filename)
+
+ @staticmethod
def vpp_ipsec_sa_set_key(node, sa_id, crypto_key, integ_key):
"""Update Security Association (SA) keys.
@@ -308,19 +380,19 @@ class IPsecUtil(object):
:param priority: SPD entry priority, higher number = higher priority.
:param action: Policy action.
:param inbound: If True policy is for inbound traffic, otherwise
- outbound.
+ outbound.
:param sa_id: SAD entry ID for protect action.
: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.
+ 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.
+ format IP/prefix or IP/mask. If no mask is provided, it's considered
+ to be /32.
:param proto: Policy selector next layer protocol number.
:param lport_range: Policy selector local TCP/UDP port range in format
- <port_start>-<port_end>.
+ <port_start>-<port_end>.
:param rport_range: Policy selector remote TCP/UDP port range in format
- <port_start>-<port_end>.
+ <port_start>-<port_end>.
:type node: dict
:type spd_id: int
:type priority: int
@@ -368,6 +440,245 @@ class IPsecUtil(object):
node['host']))
@staticmethod
+ def vpp_ipsec_spd_add_entries(node, n_entries, spd_id, priority, inbound,
+ sa_id, raddr_ip, raddr_range):
+ """Create multiple Security Policy Database entries on the VPP node.
+
+ :param node: VPP node to add SPD entries on.
+ :param n_entries: Number of SPD entries to be added.
+ :param spd_id: SPD ID to add entries on.
+ :param priority: SPD entries priority, higher number = higher priority.
+ :param inbound: If True policy is for inbound traffic, otherwise
+ outbound.
+ :param sa_id: SAD entry ID for first entry. Each subsequent entry will
+ SAD entry ID incremented by 1.
+ :param raddr_ip: Policy selector remote IPv4 start address for the first
+ entry. Remote IPv4 end address will be calculated depending on
+ raddr_range parameter. Each subsequent entry will have start address
+ next after IPv4 end address of previous entry.
+ :param raddr_range: Mask specifying range of Policy selector Remote IPv4
+ addresses. Valid values are from 1 to 32.
+ :type node: dict
+ :type n_entries: int
+ :type spd_id: int
+ :type priority: int
+ :type inbound: bool
+ :type sa_id: int
+ :type raddr_ip: string
+ :type raddr_range: int
+ """
+ tmp_filename = '/tmp/ipsec_spd_{}_add_del_entry.vat'.format(sa_id)
+ direction = 'inbound' if inbound else 'outbound'
+ addr_incr = 1 << (32 - raddr_range)
+ addr_ip = int(ip_address(unicode(raddr_ip)))
+ start_str = 'ipsec_spd_add_del_entry spd_id {0} priority {1} {2} ' \
+ 'action protect sa_id'.format(spd_id, priority, direction)
+ with open(tmp_filename, 'w') as tmp_file:
+ for i in range(0, n_entries):
+ r_ip_s = ip_address(addr_ip + addr_incr * i)
+ r_ip_e = ip_address(addr_ip + addr_incr * (i+1) - 1)
+ buf_str = '{0} {1} raddr_start {2} raddr_stop {3}\n'.format(
+ start_str, sa_id+i, r_ip_s, r_ip_e)
+ tmp_file.write(buf_str)
+ vat = VatExecutor()
+ vat.scp_and_execute_script(tmp_filename, node, 300)
+ os.remove(tmp_filename)
+
+ @staticmethod
+ def vpp_ipsec_create_tunnel_interfaces(node1, node2, if1_ip_addr,
+ if2_ip_addr, n_tunnels, crypto_alg,
+ crypto_key, integ_alg, integ_key,
+ raddr_ip1, raddr_ip2, raddr_range):
+ """Create multiple IPsec tunnel interfaces between two VPP nodes.
+
+ :param node1: VPP node 1 to create tunnel interfaces.
+ :param node2: VPP node 2 to create tunnel interfaces.
+ :param if1_ip_addr: VPP node 1 interface IP4 address.
+ :param if2_ip_addr: VPP node 2 interface IP4 address.
+ :param n_tunnels: Number of tunnell interfaces to create.
+ :param crypto_alg: The encryption algorithm name.
+ :param crypto_key: The encryption key string.
+ :param integ_alg: The integrity algorithm name.
+ :param integ_key: The integrity key string.
+ :param raddr_ip1: Policy selector remote IPv4 start address for the
+ first tunnel in direction node1->node2.
+ :param raddr_ip2: Policy selector remote IPv4 start address for the
+ 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.
+ :type node1: dict
+ :type node2: dict
+ :type if1_ip_addr: str
+ :type if2_ip_addr: str
+ :type n_tunnels: int
+ :type crypto_alg: CryptoAlg
+ :type crypto_key: str
+ :type integ_alg: IntegAlg
+ :type integ_key: str
+ :type raddr_ip1: string
+ :type raddr_ip2: string
+ :type raddr_range: int
+ """
+
+ spi_1 = 10000
+ spi_2 = 20000
+
+ raddr_ip1_i = int(ip_address(unicode(raddr_ip1)))
+ raddr_ip2_i = int(ip_address(unicode(raddr_ip2)))
+ addr_incr = 1 << (32 - raddr_range)
+
+ tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
+ tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
+
+ ckey = crypto_key.encode('hex')
+ ikey = integ_key.encode('hex')
+
+ with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
+ for i in range(0, n_tunnels):
+ if_s = 'ipsec{}'.format(i)
+ dut1_tunnel_s = 'create ipsec tunnel local-ip {0} local-spi ' \
+ '{1} remote-ip {2} remote-spi {3}\n'.format(
+ if1_ip_addr, spi_1+i, if2_ip_addr, spi_2+i)
+ tmp_f1.write(dut1_tunnel_s)
+ dut2_tunnel_s = 'create ipsec tunnel local-ip {0} local-spi ' \
+ '{1} remote-ip {2} remote-spi {3}\n'.format(
+ if2_ip_addr, spi_2+i, if1_ip_addr, spi_1+i)
+ tmp_f2.write(dut2_tunnel_s)
+ loc_c_key = 'set interface ipsec key {0} local crypto {1} ' \
+ '{2}\n'.format(if_s, crypto_alg.alg_name, ckey)
+ tmp_f1.write(loc_c_key)
+ tmp_f2.write(loc_c_key)
+ rem_c_key = 'set interface ipsec key {0} remote crypto {1} ' \
+ '{2}\n'.format(if_s, crypto_alg.alg_name, ckey)
+ tmp_f1.write(rem_c_key)
+ tmp_f2.write(rem_c_key)
+ if crypto_alg.alg_name != 'aes-gcm-128':
+ loc_i_key = 'set interface ipsec key {0} local integ {1} ' \
+ '{2}\n'.format(if_s, integ_alg.alg_name, ikey)
+ tmp_f1.write(loc_i_key)
+ tmp_f2.write(loc_i_key)
+ rem_i_key = 'set interface ipsec key {0} remote integ {1}' \
+ ' {2}\n'.format(if_s, integ_alg.alg_name, ikey)
+ tmp_f1.write(rem_i_key)
+ tmp_f2.write(rem_i_key)
+ raddr_ip1_s = ip_address(raddr_ip1_i + addr_incr*i)
+ raddr_ip2_s = ip_address(raddr_ip2_i + addr_incr*i)
+ dut1_rte_s = 'ip route add {0}/{1} via {2} {3}\n'.format(
+ raddr_ip2_s, raddr_range, if2_ip_addr, if_s)
+ tmp_f1.write(dut1_rte_s)
+ dut2_rte_s = 'ip route add {0}/{1} via {2} {3}\n'.format(
+ raddr_ip1_s, raddr_range, if1_ip_addr, if_s)
+ tmp_f2.write(dut2_rte_s)
+ up_s = 'set int state {0} up\n'.format(if_s)
+ tmp_f1.write(up_s)
+ tmp_f2.write(up_s)
+
+ vat = VatExecutor()
+ vat.scp_and_execute_cli_script(tmp_fn1, node1, 300)
+ vat.scp_and_execute_cli_script(tmp_fn2, node2, 300)
+ os.remove(tmp_fn1)
+ os.remove(tmp_fn2)
+
+ @staticmethod
+ def vpp_ipsec_add_multiple_tunnels(node1, node2, interface1, interface2,
+ n_tunnels, crypto_alg, crypto_key,
+ integ_alg, integ_key, tunnel_ip1,
+ tunnel_ip2, raddr_ip1, raddr_ip2,
+ raddr_range):
+ """Create multiple IPsec tunnels between two VPP nodes.
+
+ :param node1: VPP node 1 to create tunnels.
+ :param node2: VPP node 2 to create tunnels.
+ :param interface1: Interface name or sw_if_index on node 1.
+ :param interface2: Interface name or sw_if_index on node 2.
+ :param n_tunnels: Number of tunnels to create.
+ :param crypto_alg: The encryption algorithm name.
+ :param crypto_key: The encryption key string.
+ :param integ_alg: The integrity algorithm name.
+ :param integ_key: The integrity key string.
+ :param tunnel_ip1: Tunnel node1 IPv4 address.
+ :param tunnel_ip2: Tunnel node2 IPv4 address.
+ :param raddr_ip1: Policy selector remote IPv4 start address for the
+ first tunnel in direction node1->node2.
+ :param raddr_ip2: Policy selector remote IPv4 start address for the
+ 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.
+ :type node1: dict
+ :type node2: dict
+ :type interface1: str or int
+ :type interface2: str or int
+ :type n_tunnels: int
+ :type crypto_alg: CryptoAlg
+ :type crypto_key: str
+ :type integ_alg: str
+ :type integ_key: str
+ :type tunnel_ip1: str
+ :type tunnel_ip2: str
+ :type raddr_ip1: string
+ :type raddr_ip2: string
+ :type raddr_range: int
+ """
+ spd_id = 1
+ p_hi = 100
+ p_lo = 10
+ sa_id_1 = 10000
+ sa_id_2 = 20000
+ spi_1 = 30000
+ spi_2 = 40000
+ proto = 50
+
+ IPsecUtil.vpp_ipsec_add_spd(node1, spd_id)
+ IPsecUtil.vpp_ipsec_spd_add_if(node1, spd_id, interface1)
+ IPsecUtil.vpp_ipsec_spd_add_entry(node1, spd_id, p_hi,
+ PolicyAction.BYPASS, inbound=False,
+ proto=proto)
+ IPsecUtil.vpp_ipsec_spd_add_entry(node1, spd_id, p_hi,
+ PolicyAction.BYPASS, inbound=True,
+ proto=proto)
+
+ IPsecUtil.vpp_ipsec_add_spd(node2, spd_id)
+ IPsecUtil.vpp_ipsec_spd_add_if(node2, spd_id, interface2)
+ IPsecUtil.vpp_ipsec_spd_add_entry(node2, spd_id, p_hi,
+ PolicyAction.BYPASS, inbound=False,
+ proto=proto)
+ IPsecUtil.vpp_ipsec_spd_add_entry(node2, spd_id, p_hi,
+ PolicyAction.BYPASS, inbound=True,
+ proto=proto)
+
+ IPsecUtil.vpp_ipsec_add_sad_entries(node1, n_tunnels, sa_id_1, spi_1,
+ crypto_alg, crypto_key, integ_alg,
+ integ_key, tunnel_ip1, tunnel_ip2)
+
+ IPsecUtil.vpp_ipsec_spd_add_entries(node1, n_tunnels, spd_id, p_lo,
+ False, sa_id_1, raddr_ip2,
+ raddr_range)
+
+ IPsecUtil.vpp_ipsec_add_sad_entries(node2, n_tunnels, sa_id_1, spi_1,
+ crypto_alg, crypto_key, integ_alg,
+ integ_key, tunnel_ip1, tunnel_ip2)
+
+ IPsecUtil.vpp_ipsec_spd_add_entries(node2, n_tunnels, spd_id, p_lo,
+ True, sa_id_1, raddr_ip2,
+ raddr_range)
+
+ IPsecUtil.vpp_ipsec_add_sad_entries(node2, n_tunnels, sa_id_2, spi_2,
+ crypto_alg, crypto_key, integ_alg,
+ integ_key, tunnel_ip2, tunnel_ip1)
+
+ IPsecUtil.vpp_ipsec_spd_add_entries(node2, n_tunnels, spd_id, p_lo,
+ False, sa_id_2, raddr_ip1,
+ raddr_range)
+
+ IPsecUtil.vpp_ipsec_add_sad_entries(node1, n_tunnels, sa_id_2, spi_2,
+ crypto_alg, crypto_key, integ_alg,
+ integ_key, tunnel_ip2, tunnel_ip1)
+
+ IPsecUtil.vpp_ipsec_spd_add_entries(node1, n_tunnels, spd_id, p_lo,
+ True, sa_id_2, raddr_ip1,
+ raddr_range)
+
+ @staticmethod
def vpp_ipsec_show(node):
"""Run "show ipsec" debug CLI command.
diff --git a/resources/libraries/python/TrafficGenerator.py b/resources/libraries/python/TrafficGenerator.py
index 68bd372b84..2313ec856b 100644
--- a/resources/libraries/python/TrafficGenerator.py
+++ b/resources/libraries/python/TrafficGenerator.py
@@ -286,7 +286,6 @@ class TrafficGenerator(object):
# critical error occured
raise RuntimeError('t-rex-64 startup failed')
-
@staticmethod
def teardown_traffic_generator(node):
"""TG teardown.
@@ -394,6 +393,51 @@ class TrafficGenerator(object):
_p0, _p1, _async, _latency,
warmup_time),
timeout=int(duration)+60)
+ elif traffic_type in ["3-node-IPv4-dst-1"]:
+ (ret, stdout, stderr) = ssh.exec_command(
+ "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
+ "--duration={1} -r {2} -s {3} "
+ "--p{4}_src_start_ip 10.0.0.1 "
+ "--p{4}_dst_start_ip 20.0.0.0 "
+ "--p{4}_dst_end_ip 20.0.0.0 "
+ "--p{5}_src_start_ip 20.0.0.1 "
+ "--p{5}_dst_start_ip 10.0.0.0 "
+ "--p{5}_dst_end_ip 10.0.0.0 "
+ "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
+ duration, rate, framesize,
+ _p0, _p1, _async, _latency,
+ warmup_time),
+ timeout=int(duration)+60)
+ elif traffic_type in ["3-node-IPv4-dst-100"]:
+ (ret, stdout, stderr) = ssh.exec_command(
+ "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
+ "--duration={1} -r {2} -s {3} "
+ "--p{4}_src_start_ip 10.0.0.1 "
+ "--p{4}_dst_start_ip 20.0.0.0 "
+ "--p{4}_dst_end_ip 20.0.0.99 "
+ "--p{5}_src_start_ip 20.0.0.1 "
+ "--p{5}_dst_start_ip 10.0.0.0 "
+ "--p{5}_dst_end_ip 10.0.0.99 "
+ "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
+ duration, rate, framesize,
+ _p0, _p1, _async, _latency,
+ warmup_time),
+ timeout=int(duration)+60)
+ elif traffic_type in ["3-node-IPv4-dst-1000"]:
+ (ret, stdout, stderr) = ssh.exec_command(
+ "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
+ "--duration={1} -r {2} -s {3} "
+ "--p{4}_src_start_ip 10.0.0.1 "
+ "--p{4}_dst_start_ip 20.0.0.0 "
+ "--p{4}_dst_end_ip 20.0.3.231 "
+ "--p{5}_src_start_ip 20.0.0.1 "
+ "--p{5}_dst_start_ip 10.0.0.0 "
+ "--p{5}_dst_end_ip 10.0.3.231 "
+ "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
+ duration, rate, framesize,
+ _p0, _p1, _async, _latency,
+ warmup_time),
+ timeout=int(duration)+60)
elif traffic_type in ["3-node-IPv4-dst-10000"]:
(ret, stdout, stderr) = ssh.exec_command(
"sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
diff --git a/resources/libraries/python/VatExecutor.py b/resources/libraries/python/VatExecutor.py
index 9db53d3a36..bfb7fce4e4 100644
--- a/resources/libraries/python/VatExecutor.py
+++ b/resources/libraries/python/VatExecutor.py
@@ -84,6 +84,79 @@ class VatExecutor(object):
# TODO: download vpp_api_test output file
# self._delete_files(node, remote_file_path, remote_file_out)
+ def scp_and_execute_script(self, vat_name, node, timeout=10, json_out=True):
+ """Copy vat_name script to node, execute it and return result.
+
+ :param vat_name: Name of the vat script file.
+ Full path and name of the script is required.
+ :param node: Node to execute the VAT script on.
+ :param timeout: Seconds to allow the script to run.
+ :param json_out: Require JSON output.
+ :type vat_name: str
+ :type node: dict
+ :type timeout: int
+ :type json_out: bool
+ :returns: Status code, stdout and stderr of executed script
+ :rtype: tuple
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ ssh.scp(vat_name, vat_name)
+
+ cmd = "sudo -S {vat} {json} < {input}".format(
+ json="json" if json_out is True else "",
+ vat=Constants.VAT_BIN_NAME,
+ input=vat_name)
+ (ret_code, stdout, stderr) = ssh.exec_command(cmd, timeout)
+ self._ret_code = ret_code
+ self._stdout = stdout
+ self._stderr = stderr
+
+ logger.trace("Command '{0}' returned {1}'".format(cmd, self._ret_code))
+ logger.trace("stdout: '{0}'".format(self._stdout))
+ logger.trace("stderr: '{0}'".format(self._stderr))
+
+ self._delete_files(node, vat_name)
+
+ def scp_and_execute_cli_script(self, fname, node, timeout=10,
+ json_out=True):
+ """Copy vat_name script to node, execute it and return result.
+
+ :param fname: Name of the VPP script file.
+ Full path and name of the script is required.
+ :param node: Node to execute the VPP script on.
+ :param timeout: Seconds to allow the script to run.
+ :param json_out: Require JSON output.
+ :type vat_name: str
+ :type node: dict
+ :type timeout: int
+ :type json_out: bool
+ :returns: Status code, stdout and stderr of executed script
+ :rtype: tuple
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+
+ ssh.scp(fname, fname)
+
+ cmd = "{vat} {json}".format(json="json" if json_out is True else "",
+ vat=Constants.VAT_BIN_NAME)
+ cmd_input = "exec exec {0}".format(fname)
+ (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd, cmd_input,
+ timeout)
+ self._ret_code = ret_code
+ self._stdout = stdout
+ self._stderr = stderr
+
+ logger.trace("Command '{0}' returned {1}'".format(cmd, self._ret_code))
+ logger.trace("stdout: '{0}'".format(self._stdout))
+ logger.trace("stderr: '{0}'".format(self._stderr))
+
+ self._delete_files(node, fname)
+
def execute_script_json_out(self, vat_name, node, timeout=10):
"""Pass all arguments to 'execute_script' method, then cleanup returned
json output."""
diff --git a/resources/libraries/python/VppConfigGenerator.py b/resources/libraries/python/VppConfigGenerator.py
index d6afd1f5c7..267c47845f 100644
--- a/resources/libraries/python/VppConfigGenerator.py
+++ b/resources/libraries/python/VppConfigGenerator.py
@@ -57,6 +57,8 @@ dpdk {{
{txqueuesconfig}
}}
{pciconfig}
+ {cryptodevconfig}
+ {uiodriverconfig}
{nomultiseg}
{enablevhostuser}
}}
@@ -235,6 +237,40 @@ class VppConfigGenerator(object):
logger.debug('Setting hostname {} config with {}'.\
format(hostname, "enable-vhost-user"))
+ def add_cryptodev_config(self, node, count):
+ """Add cryptodev configuration for node.
+
+ :param node: DUT node.
+ :param count: Number of crypto device to add.
+ :type node: dict
+ :type count: int
+ :returns: nothing
+ :raises ValueError: If node type is not a DUT
+ """
+ if node['type'] != NodeType.DUT:
+ raise ValueError('Node type is not a DUT')
+ hostname = Topology.get_node_hostname(node)
+ if hostname not in self._nodeconfig:
+ self._nodeconfig[hostname] = {}
+
+ cryptodev = Topology.get_cryptodev(node)
+ cryptodev_config = 'enable-cryptodev'
+
+ for i in range(count):
+ cryptodev_config += ' dev {}'.format(\
+ re.sub(r'\d.\d$', '1.'+str(i), cryptodev))
+
+ self._nodeconfig[hostname]['cryptodev_config'] = cryptodev_config
+ logger.debug('Setting hostname {} Cryptodev config to {}'.
+ format(hostname, cryptodev_config))
+
+ uio_driver_config = 'uio-driver {}'.\
+ format(Topology.get_uio_driver(node))
+
+ self._nodeconfig[hostname]['uio_driver_config'] = uio_driver_config
+ logger.debug('Setting hostname {} uio_driver config to {}'.
+ format(hostname, uio_driver_config))
+
def remove_all_pci_devices(self, node):
"""Remove PCI device configuration from node.
@@ -280,6 +316,22 @@ class VppConfigGenerator(object):
logger.debug('Clearing Socket Memory config for hostname {}.'.
format(hostname))
+ def remove_cryptodev_config(self, node):
+ """Remove Cryptodev configuration from node.
+
+ :param node: DUT node.
+ :type node: dict
+ :returns: nothing
+ :raises ValueError: If node type is not a DUT
+ """
+ if node['type'] != NodeType.DUT:
+ raise ValueError('Node type is not a DUT')
+ hostname = Topology.get_node_hostname(node)
+ if hostname in self._nodeconfig:
+ self._nodeconfig[hostname].pop('cryptodev_config', None)
+ logger.debug('Clearing Cryptodev config for hostname {}.'.
+ format(hostname))
+
def remove_heapsize_config(self, node):
"""Remove Heap Size configuration from node.
@@ -366,6 +418,8 @@ class VppConfigGenerator(object):
txqueuesconfig = ""
nomultiseg = ""
enablevhostuser = ""
+ cryptodevconfig = ""
+ uiodriverconfig = ""
if hostname in self._nodeconfig:
cfg = self._nodeconfig[hostname]
@@ -378,6 +432,12 @@ class VppConfigGenerator(object):
if 'socketmem_config' in cfg:
socketmemconfig = cfg['socketmem_config']
+ if 'cryptodev_config' in cfg:
+ cryptodevconfig = cfg['cryptodev_config']
+
+ if 'uio_driver_config' in cfg:
+ uiodriverconfig = cfg['uio_driver_config']
+
if 'heapsize_config' in cfg:
heapsizeconfig = "\nheapsize {}\n".\
format(cfg['heapsize_config'])
@@ -393,6 +453,8 @@ class VppConfigGenerator(object):
vppconfig = VPP_CONFIG_TEMPLATE.format(cpuconfig=cpuconfig,
pciconfig=pciconfig,
+ cryptodevconfig=cryptodevconfig,
+ uiodriverconfig=uiodriverconfig,
socketmemconfig=socketmemconfig,
heapsizeconfig=heapsizeconfig,
rxqueuesconfig=rxqueuesconfig,
diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py
index 8a591dfd14..791b07053c 100644
--- a/resources/libraries/python/topology.py
+++ b/resources/libraries/python/topology.py
@@ -39,6 +39,7 @@ def load_topo_from_yaml():
# pylint: disable=invalid-name
+
class NodeType(object):
"""Defines node types used in topology dictionaries."""
# Device Under Test (this node has VPP running on it)
@@ -805,6 +806,34 @@ class Topology(object):
return node['host']
@staticmethod
+ def get_cryptodev(node):
+ """Return Crytodev configuration of the node.
+
+ :param node: Node created from topology.
+ :type node: dict
+ :return: Cryptodev configuration string.
+ :rtype: str
+ """
+ try:
+ return node['cryptodev']
+ except KeyError:
+ return None
+
+ @staticmethod
+ def get_uio_driver(node):
+ """Return uio-driver configuration of the node.
+
+ :param node: Node created from topology.
+ :type node: dict
+ :return: uio-driver configuration string.
+ :rtype: str
+ """
+ try:
+ return node['uio_driver']
+ except KeyError:
+ return None
+
+ @staticmethod
def set_interface_numa_node(node, iface_key, numa_node_id):
"""Set interface numa_node location.
diff --git a/resources/libraries/robot/default.robot b/resources/libraries/robot/default.robot
index 88d9d04cd0..08315c32ef 100644
--- a/resources/libraries/robot/default.robot
+++ b/resources/libraries/robot/default.robot
@@ -80,6 +80,43 @@
| | :FOR | ${dut} | IN | @{duts}
| | | Set VPP Scheduling rr | ${nodes['${dut}']}
+| Verify Crypto Device On All DUTs
+| | [Documentation] | Verify if Crypto QAT device virtual functions are
+| | ... | initialized on all DUTs. If parameter force_init is set to True, then
+| | ... | try to initialize.
+| | ...
+| | ... | *Arguments:*
+| | ... | - ${force_init} - Try to initialize. Type: boolean
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Verify Crypto Device On All DUTs \| ${True} \|
+| | ...
+| | [Arguments] | ${force_init}=${False}
+| | ...
+| | ${duts}= | Get Matches | ${nodes} | DUT*
+| | :FOR | ${dut} | IN | @{duts}
+| | | Crypto Device Verify | ${nodes['${dut}']} | force_init=${force_init}
+
+| Verify Kernel Module On All DUTs
+| | [Documentation] | Verify if specific kernel module is loaded on all DUTs.
+| | ... | If parameter force_load is set to True, then try to initialize.
+| | ...
+| | ... | *Arguments:*
+| | ... | - ${module} - Module to verify. Type: string
+| | ... | - ${force_load} - Try to load module. Type: boolean
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Verify Kernel Module On All DUTs \| ${True} \|
+| | ...
+| | [Arguments] | ${module} | ${force_load}=${False}
+| | ...
+| | ${duts}= | Get Matches | ${nodes} | DUT*
+| | :FOR | ${dut} | IN | @{duts}
+| | | Kernel Module Verify | ${nodes['${dut}']} | ${module}
+| | | ... | force_load=${force_load}
+
| Add '${m}' worker threads and rxqueues '${n}' in 3-node single-link topo
| | [Documentation] | Setup M worker threads and N rxqueues in vpp startup\
| | ... | configuration on all DUTs in 3-node single-link topology.
@@ -244,6 +281,22 @@
| | :FOR | ${dut} | IN | @{duts}
| | | Add Enable Vhost User Config | ${nodes['${dut}']}
+| Add Cryptodev to all DUTs
+| | [Documentation] | AddCryptodev to VPP startup configuration to all
+| | ... | DUTs
+| | ...
+| | ... | *Arguments:*
+| | ... | - ${count} - Number of QAT devices. Type: integer
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Add Cryptodev to all DUTs \| ${4} \|
+| | ...
+| | [Arguments] | ${count}
+| | ${duts}= | Get Matches | ${nodes} | DUT*
+| | :FOR | ${dut} | IN | @{duts}
+| | | Add Cryptodev Config | ${nodes['${dut}']} | ${count}
+
| Remove startup configuration of VPP from all DUTs
| | [Documentation] | Remove VPP startup configuration from all DUTs.
| | ...
@@ -252,6 +305,7 @@
| | | Remove All PCI Devices | ${nodes['${dut}']}
| | | Remove All CPU Config | ${nodes['${dut}']}
| | | Remove Socketmem Config | ${nodes['${dut}']}
+| | | Remove Cryptodev Config | ${nodes['${dut}']}
| | | Remove Heapsize Config | ${nodes['${dut}']}
| | | Remove Rxqueues Config | ${nodes['${dut}']}
| | | Remove No Multi Seg Config | ${nodes['${dut}']}
diff --git a/resources/libraries/robot/ipsec.robot b/resources/libraries/robot/ipsec.robot
index 92b292ede3..9c9980341f 100644
--- a/resources/libraries/robot/ipsec.robot
+++ b/resources/libraries/robot/ipsec.robot
@@ -16,7 +16,7 @@
| Library | resources.libraries.python.IPsecUtil
| Library | resources.libraries.python.NodePath
| Library | resources.libraries.python.TrafficScriptExecutor
-| Library | resources.libraries.python.IPv4Util
+| Library | resources.libraries.python.IPv4Util.IPv4Util
| Library | resources.libraries.python.InterfaceUtil
| Library | resources.libraries.python.Routing
| Library | String
diff --git a/resources/libraries/robot/performance.robot b/resources/libraries/robot/performance.robot
index 47c43d2a5c..ee662841c9 100644
--- a/resources/libraries/robot/performance.robot
+++ b/resources/libraries/robot/performance.robot
@@ -112,8 +112,8 @@
| | ... | - dut1_if1 - DUT1 interface towards TG.
| | ... | - dut1_if2 - DUT1 interface towards DUT2.
| | ... | - dut2 - DUT2 node
-| | ... | - dut2_if1 - DUT2 interface towards TG.
-| | ... | - dut2_if2 - DUT2 interface towards DUT1.
+| | ... | - dut2_if1 - DUT2 interface towards DUT1.
+| | ... | - dut2_if2 - DUT2 interface towards TG.
| | ...
| | Append Nodes | ${nodes['TG']} | ${nodes['DUT1']} | ${nodes['DUT2']}
| | ... | ${nodes['TG']}
@@ -244,6 +244,49 @@
| | Vpp Node Interfaces Ready Wait | ${dut1}
| | Vpp Node Interfaces Ready Wait | ${dut2}
+| IPsec initialized in a 3-node circular topology
+| | [Documentation]
+| | ... | Set UP state on VPP interfaces in path on nodes in 3-node circular
+| | ... | topology. Get the interface MAC addresses and setup ARP on all VPP
+| | ... | interfaces. Setup IPv4 addresses with /24 prefix on DUT-TG and
+| | ... | DUT1-DUT2 links. Set routing for encrypted traffic on both DUT nodes
+| | ... | with prefix /8 and next hop of neighbour DUT or TG interface IPv4
+| | ... | address.
+| | ...
+| | VPP Show Crypto Device Mapping | ${dut1}
+| | VPP Show Crypto Device Mapping | ${dut2}
+| | VPP interfaces in path are up in a 3-node circular topology
+| | ${tg_if1_mac}= | Get Interface MAC | ${tg} | ${tg_if1}
+| | ${tg_if2_mac}= | Get Interface MAC | ${tg} | ${tg_if2}
+| | ${dut1_if1_mac}= | Get Interface MAC | ${dut1} | ${dut1_if1}
+| | ${dut1_if2_mac}= | Get Interface MAC | ${dut1} | ${dut1_if2}
+| | ${dut2_if1_mac}= | Get Interface MAC | ${dut2} | ${dut2_if1}
+| | ${dut2_if2_mac}= | Get Interface MAC | ${dut2} | ${dut2_if2}
+| | Set Interface State | ${dut1} | ${dut1_if1} | up
+| | Set Interface State | ${dut1} | ${dut1_if2} | up
+| | Set Interface State | ${dut2} | ${dut2_if1} | up
+| | Set Interface State | ${dut2} | ${dut2_if2} | up
+| | Set Test Variable | ${tg_if1_mac}
+| | Set Test Variable | ${tg_if2_mac}
+| | Set Test Variable | ${dut1_if1_mac}
+| | Set Test Variable | ${dut1_if2_mac}
+| | Set Test Variable | ${dut2_if1_mac}
+| | Set Test Variable | ${dut2_if2_mac}
+| | IP addresses are set on interfaces | ${dut1} | ${dut1_if1} | ${dut1_if1_ip4}
+| | ... | 24
+| | IP addresses are set on interfaces | ${dut1} | ${dut1_if2} | ${dut1_if2_ip4}
+| | ... | 24
+| | IP addresses are set on interfaces | ${dut2} | ${dut2_if1} | ${dut2_if1_ip4}
+| | ... | 24
+| | IP addresses are set on interfaces | ${dut2} | ${dut2_if2} | ${dut2_if2_ip4}
+| | ... | 24
+| | Add arp on dut | ${dut1} | ${dut1_if1} | ${tg_if1_ip4} | ${tg_if1_mac}
+| | Add arp on dut | ${dut1} | ${dut1_if2} | ${dut2_if1_ip4} | ${dut2_if1_mac}
+| | Add arp on dut | ${dut2} | ${dut2_if2} | ${tg_if2_ip4} | ${tg_if2_mac}
+| | Add arp on dut | ${dut2} | ${dut2_if1} | ${dut1_if2_ip4} | ${dut1_if2_mac}
+| | Vpp Route Add | ${dut1} | ${laddr_ip4} | 8 | ${tg_if1_ip4} | ${dut1_if1}
+| | Vpp Route Add | ${dut2} | ${raddr_ip4} | 8 | ${tg_if2_ip4} | ${dut2_if2}
+
| IPv4 forwarding initialized in a 3-node circular topology
| | [Documentation]
| | ... | Set UP state on VPP interfaces in path on nodes in 3-node circular
@@ -1101,7 +1144,6 @@
| | Show vpp version on all DUTs
| | 2-node circular Topology Variables Setup with DUT interface model
| | ... | ${nic_model}
-| | Setup 2-node startup configuration of VPP on all DUTs
| | Initialize traffic generator | ${tg} | ${tg_if1} | ${tg_if2}
| | ... | ${dut1} | ${dut1_if1} | ${dut1} | ${dut1_if2} | ${topology_type}
@@ -1129,7 +1171,6 @@
| | Show vpp version on all DUTs
| | 2-node circular Topology Variables Setup with DUT interface model
| | ... | ${nic_model}
-| | Setup 2-node startup configuration of VPP on all DUTs
| | Initialize traffic generator | ${tg} | ${tg_if1} | ${tg_if2}
| | ... | ${dut1} | ${dut1_if1} | ${dut1} | ${dut1_if2} | ${topology_type}
| | ... | ${tg_if1_dest_mac} | ${tg_if2_dest_mac}
@@ -1154,7 +1195,6 @@
| | Show vpp version on all DUTs
| | 3-node circular Topology Variables Setup with DUT interface model
| | ... | ${nic_model}
-| | Setup default startup configuration of VPP on all DUTs
| | Initialize traffic generator | ${tg} | ${tg_if1} | ${tg_if2}
| | ... | ${dut1} | ${dut1_if1} | ${dut2} | ${dut2_if2} | ${topology_type}
@@ -2324,7 +2364,6 @@
| Performance test setup
| | [Documentation] | Common test setup for performance tests.
| | ...
-| | Setup all DUTs before test
| | Reset VAT History On All DUTs | ${nodes}
| Performance test teardown
diff --git a/resources/templates/vat/show_crypto_device_mapping.vat b/resources/templates/vat/show_crypto_device_mapping.vat
new file mode 100644
index 0000000000..d25e1e6dd8
--- /dev/null
+++ b/resources/templates/vat/show_crypto_device_mapping.vat
@@ -0,0 +1 @@
+exec show crypto device mapping