From 4e421686933355ddc6ce0780efd15041c60de7e3 Mon Sep 17 00:00:00 2001 From: Kirill Rybalchenko Date: Wed, 21 Dec 2016 12:16:29 +0000 Subject: IPsec Multi-Tunnel performance test suite Change-Id: I4b0ba83960e50089f29cab9a30ab760241c6f566 Signed-off-by: Kirill Rybalchenko --- resources/libraries/python/DUTSetup.py | 184 +++++++++++++ resources/libraries/python/IPsecUtil.py | 331 ++++++++++++++++++++++- resources/libraries/python/TrafficGenerator.py | 46 +++- resources/libraries/python/VatExecutor.py | 73 +++++ resources/libraries/python/VppConfigGenerator.py | 62 +++++ resources/libraries/python/topology.py | 29 ++ resources/libraries/robot/default.robot | 54 ++++ resources/libraries/robot/ipsec.robot | 2 +- resources/libraries/robot/performance.robot | 51 +++- 9 files changed, 814 insertions(+), 18 deletions(-) (limited to 'resources/libraries') 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 @@ -114,6 +117,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. @@ -172,6 +184,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 @@ -236,6 +257,57 @@ class IPsecUtil(object): out[0], 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 - -. + -. :param rport_range: Policy selector remote TCP/UDP port range in format - -. + -. :type node: dict :type spd_id: int :type priority: int @@ -367,6 +439,245 @@ class IPsecUtil(object): err_msg='Add entry to SPD {0} failed on {1}'.format(spd_id, 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) @@ -804,6 +805,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 -- cgit 1.2.3-korg