diff options
Diffstat (limited to 'resources/libraries/python')
-rw-r--r-- | resources/libraries/python/CpuUtils.py | 6 | ||||
-rw-r--r-- | resources/libraries/python/DPDK/L3fwdTest.py | 22 | ||||
-rw-r--r-- | resources/libraries/python/DPDK/TestpmdTest.py | 20 | ||||
-rw-r--r-- | resources/libraries/python/HoststackUtil.py | 21 | ||||
-rw-r--r-- | resources/libraries/python/IPsecUtil.py | 497 | ||||
-rw-r--r-- | resources/libraries/python/KubernetesUtils.py | 2 | ||||
-rw-r--r-- | resources/libraries/python/QemuManager.py | 28 | ||||
-rw-r--r-- | resources/libraries/python/QemuUtils.py | 16 | ||||
-rw-r--r-- | resources/libraries/python/VppConfigGenerator.py | 65 | ||||
-rw-r--r-- | resources/libraries/python/autogen/Regenerator.py | 3 | ||||
-rw-r--r-- | resources/libraries/python/enum_util.py | 70 |
11 files changed, 449 insertions, 301 deletions
diff --git a/resources/libraries/python/CpuUtils.py b/resources/libraries/python/CpuUtils.py index c77d0f83b1..518469bd31 100644 --- a/resources/libraries/python/CpuUtils.py +++ b/resources/libraries/python/CpuUtils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # 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: @@ -247,6 +247,9 @@ class CpuUtils: """Return list of DUT node related list of CPU numbers. The main computing unit is physical core count. + On SMT enabled DUTs, both sibling logical cores are used, + unless Robot variable \${smt_used} is set to False. + :param node: DUT node. :param cpu_node: Numa node number. :param nf_chains: Number of NF chains. @@ -278,6 +281,7 @@ class CpuUtils: raise RuntimeError(u"NodeID is out of range!") smt_used = CpuUtils.is_smt_enabled(node[u"cpuinfo"]) + smt_used = BuiltIn().get_variable_value("\${smt_used}", smt_used) cpu_list = CpuUtils.cpu_list_per_node(node, cpu_node, smt_used) # CPU thread sibling offset. sib = len(cpu_list) // CpuUtils.NR_OF_THREADS diff --git a/resources/libraries/python/DPDK/L3fwdTest.py b/resources/libraries/python/DPDK/L3fwdTest.py index 178c747da5..f7da7bdb3b 100644 --- a/resources/libraries/python/DPDK/L3fwdTest.py +++ b/resources/libraries/python/DPDK/L3fwdTest.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # 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: @@ -31,7 +31,7 @@ class L3fwdTest: @staticmethod def start_l3fwd_on_all_duts( - nodes, topology_info, phy_cores, rx_queues=None, jumbo_frames=False, + nodes, topology_info, phy_cores, rx_queues=None, jumbo=False, rxd=None, txd=None): """ Execute the l3fwd on all dut nodes. @@ -40,7 +40,7 @@ class L3fwdTest: :param topology_info: All the info from the topology file. :param phy_cores: Number of physical cores to use. :param rx_queues: Number of RX queues. - :param jumbo_frames: Jumbo frames on/off. + :param jumbo: Jumbo frames on/off. :param rxd: Number of RX descriptors. :param txd: Number of TX descriptors. @@ -48,7 +48,7 @@ class L3fwdTest: :type topology_info: dict :type phy_cores: int :type rx_queues: int - :type jumbo_frames: bool + :type jumbo: bool :type rxd: int :type txd: int :raises RuntimeError: If bash return code is not 0. @@ -76,7 +76,7 @@ class L3fwdTest: L3fwdTest.start_l3fwd( nodes, node, if1=if1, if2=if2, lcores_list=cpu_dp, nb_cores=dp_count_int, queue_nums=rxq_count_int, - jumbo_frames=jumbo_frames, tg_flip=tg_flip + jumbo=jumbo, tg_flip=tg_flip ) for node in nodes: if u"DUT" in node: @@ -88,7 +88,7 @@ class L3fwdTest: L3fwdTest.start_l3fwd( nodes, nodes[node], if1=if1, if2=if2, lcores_list=cpu_dp, nb_cores=dp_count_int, - queue_nums=rxq_count_int, jumbo_frames=jumbo_frames, + queue_nums=rxq_count_int, jumbo=jumbo, tg_flip=tg_flip ) else: @@ -98,7 +98,7 @@ class L3fwdTest: @staticmethod def start_l3fwd( nodes, node, if1, if2, lcores_list, nb_cores, queue_nums, - jumbo_frames, tg_flip): + jumbo, tg_flip): """ Execute the l3fwd on the dut_node. @@ -113,7 +113,7 @@ class L3fwdTest: :param lcores_list: The lcore list string for the l3fwd routing :param nb_cores: The cores number for the forwarding :param queue_nums: The queues number for the NIC - :param jumbo_frames: Indication if the jumbo frames are used (True) or + :param jumbo: Indication if the jumbo frames are used (True) or not (False). :param tg_flip: Whether TG ports are reordered. :type nodes: dict @@ -123,7 +123,7 @@ class L3fwdTest: :type lcores_list: str :type nb_cores: str :type queue_nums: str - :type jumbo_frames: bool + :type jumbo: bool :type tg_flip: bool """ if node[u"type"] == NodeType.DUT: @@ -144,7 +144,7 @@ class L3fwdTest: f"({port}, {queue}, {lcores[index % NB_PORTS]})," index += 1 - if jumbo_frames: + if jumbo: l3fwd_args = DpdkUtil.get_l3fwd_args( eal_corelist=f"1,{lcores_list}", eal_driver=False, @@ -155,7 +155,7 @@ class L3fwdTest: pmd_eth_dest_0=f"\\\"0,{adj_mac0}\\\"", pmd_eth_dest_1=f"\\\"1,{adj_mac1}\\\"", pmd_parse_ptype=True, - pmd_max_pkt_len=jumbo_frames + pmd_max_pkt_len=jumbo ) else: l3fwd_args = DpdkUtil.get_l3fwd_args( diff --git a/resources/libraries/python/DPDK/TestpmdTest.py b/resources/libraries/python/DPDK/TestpmdTest.py index 3baba30715..c141851bdc 100644 --- a/resources/libraries/python/DPDK/TestpmdTest.py +++ b/resources/libraries/python/DPDK/TestpmdTest.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # 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: @@ -30,7 +30,7 @@ class TestpmdTest: @staticmethod def start_testpmd_on_all_duts( - nodes, topology_info, phy_cores, rx_queues=None, jumbo_frames=False, + nodes, topology_info, phy_cores, rx_queues=None, jumbo=False, rxd=None, txd=None, nic_rxq_size=None, nic_txq_size=None): """ Start the testpmd with M worker threads and rxqueues N and jumbo @@ -40,7 +40,7 @@ class TestpmdTest: :param topology_info: All the info from the topology file. :param phy_cores: Number of physical cores to use. :param rx_queues: Number of RX queues. - :param jumbo_frames: Jumbo frames on/off. + :param jumbo: Jumbo frames on/off. :param rxd: Number of RX descriptors. :param txd: Number of TX descriptors. :param nic_rxq_size: RX queue size. @@ -50,7 +50,7 @@ class TestpmdTest: :type topology_info: dict :type phy_cores: int :type rx_queues: int - :type jumbo_frames: bool + :type jumbo: bool :type rxd: int :type txd: int :type nic_rxq_size: int @@ -80,7 +80,7 @@ class TestpmdTest: TestpmdTest.start_testpmd( node, if1=if1, if2=if2, lcores_list=cpu_dp, nb_cores=dp_count_int, queue_nums=rxq_count_int, - jumbo_frames=jumbo_frames, rxq_size=nic_rxq_size, + jumbo=jumbo, rxq_size=nic_rxq_size, txq_size=nic_txq_size ) for node in nodes: @@ -99,7 +99,7 @@ class TestpmdTest: nodes[node], if1=if1, if2=if2, lcores_list=cpu_dp, nb_cores=dp_count_int, queue_nums=rxq_count_int, - jumbo_frames=jumbo_frames, + jumbo=jumbo, rxq_size=nic_rxq_size, txq_size=nic_txq_size ) else: @@ -109,7 +109,7 @@ class TestpmdTest: @staticmethod def start_testpmd( node, if1, if2, lcores_list, nb_cores, queue_nums, - jumbo_frames, rxq_size=1024, txq_size=1024): + jumbo, rxq_size=1024, txq_size=1024): """ Execute the testpmd on the DUT node. @@ -119,7 +119,7 @@ class TestpmdTest: :param lcores_list: The DPDK run cores. :param nb_cores: The cores number for the forwarding. :param queue_nums: The queues number for the NIC. - :param jumbo_frames: Indication if the jumbo frames are used (True) or + :param jumbo: Indication if the jumbo frames are used (True) or not (False). :param rxq_size: RXQ size. Default=1024. :param txq_size: TXQ size. Default=1024. @@ -129,7 +129,7 @@ class TestpmdTest: :type lcores_list: str :type nb_cores: int :type queue_nums: str - :type jumbo_frames: bool + :type jumbo: bool :type rxq_size: int :type txq_size: int :raises RuntimeError: If the script "run_testpmd.sh" fails. @@ -138,7 +138,7 @@ class TestpmdTest: if_pci0 = Topology.get_interface_pci_addr(node, if1) if_pci1 = Topology.get_interface_pci_addr(node, if2) - pmd_max_pkt_len = u"9200" if jumbo_frames else u"1518" + pmd_max_pkt_len = u"9200" if jumbo else u"1518" testpmd_args = DpdkUtil.get_testpmd_args( eal_corelist=f"1,{lcores_list}", eal_driver=False, diff --git a/resources/libraries/python/HoststackUtil.py b/resources/libraries/python/HoststackUtil.py index 399395d41a..4ac73ff924 100644 --- a/resources/libraries/python/HoststackUtil.py +++ b/resources/libraries/python/HoststackUtil.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # 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: @@ -413,10 +413,6 @@ class HoststackUtil(): f"bits/sec, pkt-drop-rate {nsim_attr[u'packets_per_drop']} " \ f"pkts/drop\n" - test_results += \ - f"\n{role} VPP 'show errors' on host {node[u'host']}:\n" \ - f"{PapiSocketExecutor.run_cli_cmd(node, u'show error')}\n" - if u"error" in program_stderr.lower(): test_results += f"ERROR DETECTED:\n{program_stderr}" return (True, test_results) @@ -469,18 +465,3 @@ class HoststackUtil(): :rtype: bool """ return server_defer_fail and client_defer_fail - - @staticmethod - def log_vpp_hoststack_data(node): - """Retrieve and log VPP HostStack data. - - :param node: DUT node. - :type node: dict - :raises RuntimeError: If node subtype is not a DUT or startup failed. - """ - - if node[u"type"] != u"DUT": - raise RuntimeError(u"Node type is not a DUT!") - - PapiSocketExecutor.run_cli_cmd(node, u"show error") - PapiSocketExecutor.run_cli_cmd(node, u"show interface") diff --git a/resources/libraries/python/IPsecUtil.py b/resources/libraries/python/IPsecUtil.py index 19995e547d..1abfee2cec 100644 --- a/resources/libraries/python/IPsecUtil.py +++ b/resources/libraries/python/IPsecUtil.py @@ -24,6 +24,7 @@ from typing import Iterable, List, Optional, Sequence, Tuple, Union from robot.libraries.BuiltIn import BuiltIn from resources.libraries.python.Constants import Constants +from resources.libraries.python.enum_util import get_enum_instance from resources.libraries.python.IncrementUtil import ObjIncrement from resources.libraries.python.InterfaceUtil import ( InterfaceUtil, @@ -60,31 +61,59 @@ def gen_key(length: int) -> bytes: ) -class PolicyAction(Enum): - """Policy actions.""" +# TODO: Introduce a metaclass that adds .find and .InputType automatically? +class IpsecSpdAction(Enum): + """IPsec SPD actions. - BYPASS = ("bypass", 0) + Mirroring VPP: src/vnet/ipsec/ipsec_types.api enum ipsec_spd_action. + """ + + BYPASS = NONE = ("bypass", 0) DISCARD = ("discard", 1) + RESOLVE = ("resolve", 2) PROTECT = ("protect", 3) - def __init__(self, policy_name: str, policy_int_repr: int): - self.policy_name = policy_name - self.policy_int_repr = policy_int_repr + def __init__(self, action_name: str, action_int_repr: int): + self.action_name = action_name + self.action_int_repr = action_int_repr def __str__(self) -> str: - return self.policy_name + return self.action_name def __int__(self) -> int: - return self.policy_int_repr + return self.action_int_repr class CryptoAlg(Enum): - """Encryption algorithms.""" + """Encryption algorithms. + + API names and numeric enums from ipsec_types.api (enum ipsec_crypto_alg). + Lowercase names from ipsec_sa.h (foreach_ipsec_crypto_alg). + + Scapy names are from: + https://github.com/secdev/scapy/blob/master/scapy/layers/ipsec.py + + Key lengths from crypto.h + (foreach_crypto_cipher_alg and foreach_crypto_aead_alg). + """ + + NONE = ("none", 0, "none", 0) AES_CBC_128 = ("aes-cbc-128", 1, "AES-CBC", 16) + AES_CBC_192 = ("aes-cbc-192", 2, "AES-CBC", 24) AES_CBC_256 = ("aes-cbc-256", 3, "AES-CBC", 32) + AES_CTR_128 = ("aes-ctr-128", 4, "AES-CTR", 16) + AES_CTR_192 = ("aes-ctr-192", 5, "AES-CTR", 24) + AES_CTR_256 = ("aes-ctr-256", 6, "AES-CTR", 32) AES_GCM_128 = ("aes-gcm-128", 7, "AES-GCM", 16) + AES_GCM_192 = ("aes-gcm-192", 8, "AES-GCM", 24) AES_GCM_256 = ("aes-gcm-256", 9, "AES-GCM", 32) + DES_CBC = ("des-cbc", 10, "DES", 7) + _3DES_CBC = ("3des-cbc", 11, "3DES", 24) + CHACHA20_POLY1305 = ("chacha20-poly1305", 12, "CHACHA20-POLY1305", 32) + AES_NULL_GMAC_128 = ("aes-null-gmac-128", 13, "AES-NULL-GMAC", 16) + AES_NULL_GMAC_192 = ("aes-null-gmac-192", 14, "AES-NULL-GMAC", 24) + AES_NULL_GMAC_256 = ("aes-null-gmac-256", 15, "AES-NULL-GMAC", 32) def __init__( self, alg_name: str, alg_int_repr: int, scapy_name: str, key_len: int @@ -94,11 +123,37 @@ class CryptoAlg(Enum): self.scapy_name = scapy_name self.key_len = key_len + # TODO: Investigate if __int__ works with PAPI. It was not enough for "if". + def __bool__(self): + """A shorthand to enable "if crypto_alg:" constructs.""" + return self.alg_int_repr != 0 + class IntegAlg(Enum): - """Integrity algorithm.""" + """Integrity algorithms. + + API names and numeric enums from ipsec_types.api (enum ipsec_integ_alg). + + Lowercase names from ipsec_sa.h (foreach_ipsec_integ_alg). + + Scapy names are from: + https://github.com/secdev/scapy/blob/master/scapy/layers/ipsec.py + Among those, "AES-CMAC-96" may be a mismatch, + but there is no sha2-related item with "96" in it. + Key lengths seem to be given double of digest length + from crypto.h (foreach_crypto_link_async_alg), + but data there is not complete + (e.g. it does not distinguish sha-256-96 from sha-256-128). + The missing values are chosen based on last number (e.g. 192 / 4 = 48). + """ + + NONE = ("none", 0, "none", 0) + MD5_96 = ("md5-96", 1, "HMAC-MD5-96", 24) + SHA1_96 = ("sha1-96", 2, "HMAC-SHA1-96", 24) + SHA_256_96 = ("sha-256-96", 3, "AES-CMAC-96", 24) SHA_256_128 = ("sha-256-128", 4, "SHA2-256-128", 32) + SHA_384_192 = ("sha-384-192", 5, "SHA2-384-192", 48) SHA_512_256 = ("sha-512-256", 6, "SHA2-512-256", 64) def __init__( @@ -109,18 +164,44 @@ class IntegAlg(Enum): self.scapy_name = scapy_name self.key_len = key_len + def __bool__(self): + """A shorthand to enable "if integ_alg:" constructs.""" + return self.alg_int_repr != 0 + +# TODO: Base on Enum, so str values can be defined as in alg enums? class IPsecProto(IntEnum): - """IPsec protocol.""" + """IPsec protocol. + + Mirroring VPP: src/vnet/ipsec/ipsec_types.api enum ipsec_proto. + """ - IPSEC_API_PROTO_ESP = 50 - IPSEC_API_PROTO_AH = 51 + ESP = 50 + AH = 51 + NONE = 255 + def __str__(self) -> str: + """Return string suitable for CLI commands. + + None is not supported. + + :returns: Lowercase name of the proto. + :rtype: str + :raises: ValueError if the numeric value is not recognized. + """ + num = int(self) + if num == 50: + return "esp" + if num == 51: + return "ah" + raise ValueError(f"String form not defined for IPsecProto {num}") + +# The rest of enums do not appear outside this file, so no no change needed yet. class IPsecSadFlags(IntEnum): """IPsec Security Association Database flags.""" - IPSEC_API_SAD_FLAG_NONE = 0 + IPSEC_API_SAD_FLAG_NONE = NONE = 0 # Enable extended sequence numbers IPSEC_API_SAD_FLAG_USE_ESN = 0x01 # Enable Anti - replay @@ -139,7 +220,7 @@ class IPsecSadFlags(IntEnum): class TunnelEncpaDecapFlags(IntEnum): """Flags controlling tunnel behaviour.""" - TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0 + TUNNEL_API_ENCAP_DECAP_FLAG_NONE = NONE = 0 # at encap, copy the DF bit of the payload into the tunnel header TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1 # at encap, set the DF bit in the tunnel header @@ -156,177 +237,93 @@ class TunnelMode(IntEnum): """Tunnel modes.""" # point-to-point - TUNNEL_API_MODE_P2P = 0 + TUNNEL_API_MODE_P2P = NONE = 0 # multi-point TUNNEL_API_MODE_MP = 1 -class IPsecUtil: - """IPsec utilities.""" - - @staticmethod - def policy_action_bypass() -> PolicyAction: - """Return policy action bypass. - - :returns: PolicyAction enum BYPASS object. - :rtype: PolicyAction - """ - return PolicyAction.BYPASS - - @staticmethod - def policy_action_discard() -> PolicyAction: - """Return policy action discard. - - :returns: PolicyAction enum DISCARD object. - :rtype: PolicyAction - """ - return PolicyAction.DISCARD - - @staticmethod - def policy_action_protect() -> PolicyAction: - """Return policy action protect. - - :returns: PolicyAction enum PROTECT object. - :rtype: PolicyAction - """ - return PolicyAction.PROTECT - - @staticmethod - def crypto_alg_aes_cbc_128() -> CryptoAlg: - """Return encryption algorithm aes-cbc-128. - - :returns: CryptoAlg enum AES_CBC_128 object. - :rtype: CryptoAlg - """ - return CryptoAlg.AES_CBC_128 - - @staticmethod - def crypto_alg_aes_cbc_256() -> CryptoAlg: - """Return encryption algorithm aes-cbc-256. - - :returns: CryptoAlg enum AES_CBC_256 object. - :rtype: CryptoAlg - """ - return CryptoAlg.AES_CBC_256 - - @staticmethod - def crypto_alg_aes_gcm_128() -> CryptoAlg: - """Return encryption algorithm aes-gcm-128. +# Derived types for type hints, based on capabilities of get_enum_instance. +IpsecSpdAction.InputType = Union[IpsecSpdAction, str, None] +CryptoAlg.InputType = Union[CryptoAlg, str, None] +IntegAlg.InputType = Union[IntegAlg, str, None] +IPsecProto.InputType = Union[IPsecProto, str, int, None] +# TODO: Introduce a metaclass that adds .find and .InputType automatically? - :returns: CryptoAlg enum AES_GCM_128 object. - :rtype: CryptoAlg - """ - return CryptoAlg.AES_GCM_128 - @staticmethod - def crypto_alg_aes_gcm_256() -> CryptoAlg: - """Return encryption algorithm aes-gcm-256. +class IPsecUtil: + """IPsec utilities.""" - :returns: CryptoAlg enum AES_GCM_128 object. - :rtype: CryptoAlg - """ - return CryptoAlg.AES_GCM_256 + # The following 4 methods are Python one-liners, + # but they are useful when called as a Robot keyword. @staticmethod - def get_crypto_alg_key_len(crypto_alg: CryptoAlg) -> int: + def get_crypto_alg_key_len(crypto_alg: CryptoAlg.InputType) -> int: """Return encryption algorithm key length. + This is a Python one-liner, but useful when called as a Robot keyword. + :param crypto_alg: Encryption algorithm. - :type crypto_alg: CryptoAlg + :type crypto_alg: CryptoAlg.InputType :returns: Key length. :rtype: int """ - return crypto_alg.key_len + return get_enum_instance(CryptoAlg, crypto_alg).key_len @staticmethod - def get_crypto_alg_scapy_name(crypto_alg: CryptoAlg) -> str: + def get_crypto_alg_scapy_name(crypto_alg: CryptoAlg.InputType) -> str: """Return encryption algorithm scapy name. + This is a Python one-liner, but useful when called as a Robot keyword. + :param crypto_alg: Encryption algorithm. - :type crypto_alg: CryptoAlg + :type crypto_alg: CryptoAlg.InputType :returns: Algorithm scapy name. :rtype: str """ - return crypto_alg.scapy_name - - @staticmethod - def integ_alg_sha_256_128() -> IntegAlg: - """Return integrity algorithm SHA-256-128. - - :returns: IntegAlg enum SHA_256_128 object. - :rtype: IntegAlg - """ - return IntegAlg.SHA_256_128 + return get_enum_instance(CryptoAlg, crypto_alg).scapy_name + # The below to keywords differ only by enum type conversion from str. @staticmethod - def integ_alg_sha_512_256() -> IntegAlg: - """Return integrity algorithm SHA-512-256. - - :returns: IntegAlg enum SHA_512_256 object. - :rtype: IntegAlg - """ - return IntegAlg.SHA_512_256 - - @staticmethod - def get_integ_alg_key_len(integ_alg: Optional[IntegAlg]) -> int: + def get_integ_alg_key_len(integ_alg: IntegAlg.InputType) -> int: """Return integrity algorithm key length. - None argument is accepted, returning zero. - :param integ_alg: Integrity algorithm. - :type integ_alg: Optional[IntegAlg] + :type integ_alg: IntegAlg.InputType :returns: Key length. :rtype: int """ - return 0 if integ_alg is None else integ_alg.key_len + return get_enum_instance(IntegAlg, integ_alg).key_len @staticmethod - def get_integ_alg_scapy_name(integ_alg: Optional[IntegAlg]) -> str: + def get_integ_alg_scapy_name(integ_alg: IntegAlg.InputType) -> str: """Return integrity algorithm scapy name. :param integ_alg: Integrity algorithm. - :type integ_alg: IntegAlg + :type integ_alg: IntegAlg.InputType :returns: Algorithm scapy name. :rtype: str """ - return integ_alg.scapy_name - - @staticmethod - def ipsec_proto_esp() -> int: - """Return IPSec protocol ESP. - - :returns: IPsecProto enum ESP object. - :rtype: IPsecProto - """ - return int(IPsecProto.IPSEC_API_PROTO_ESP) - - @staticmethod - def ipsec_proto_ah() -> int: - """Return IPSec protocol AH. - - :returns: IPsecProto enum AH object. - :rtype: IPsecProto - """ - return int(IPsecProto.IPSEC_API_PROTO_AH) + return get_enum_instance(IntegAlg, integ_alg).scapy_name @staticmethod def vpp_ipsec_select_backend( - node: dict, protocol: int, index: int = 1 + node: dict, proto: IPsecProto.InputType, index: int = 1 ) -> None: """Select IPsec backend. :param node: VPP node to select IPsec backend on. - :param protocol: IPsec protocol. + :param proto: IPsec protocol. :param index: Backend index. :type node: dict - :type protocol: IPsecProto + :type proto: IPsecProto.InputType :type index: int :raises RuntimeError: If failed to select IPsec backend or if no API reply received. """ + proto = get_enum_instance(IPsecProto, proto) cmd = "ipsec_select_backend" err_msg = f"Failed to select IPsec backend on host {node['host']}" - args = dict(protocol=protocol, index=index) + args = dict(protocol=proto, index=index) with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args).get_reply(err_msg) @@ -420,9 +417,9 @@ class IPsecUtil: node: dict, sad_id: int, spi: int, - crypto_alg: CryptoAlg, - crypto_key: str, - integ_alg: Optional[IntegAlg] = None, + crypto_alg: CryptoAlg.InputType = None, + crypto_key: str = "", + integ_alg: IntegAlg.InputType = None, integ_key: str = "", tunnel_src: Optional[str] = None, tunnel_dst: Optional[str] = None, @@ -443,13 +440,15 @@ class IPsecUtil: :type node: dict :type sad_id: int :type spi: int - :type crypto_alg: CryptoAlg + :type crypto_alg: CryptoAlg.InputType :type crypto_key: str - :type integ_alg: Optional[IntegAlg] + :type integ_alg: IntegAlg.InputType :type integ_key: str :type tunnel_src: Optional[str] :type tunnel_dst: Optional[str] """ + crypto_alg = get_enum_instance(CryptoAlg, crypto_alg) + integ_alg = get_enum_instance(IntegAlg, integ_alg) if isinstance(crypto_key, str): crypto_key = crypto_key.encode(encoding="utf-8") if isinstance(integ_key, str): @@ -480,7 +479,7 @@ class IPsecUtil: spi=int(spi), crypto_algorithm=crypto_alg.alg_int_repr, crypto_key=ckey, - integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0, + integrity_algorithm=integ_alg.alg_int_repr, integrity_key=ikey, flags=flags, tunnel=dict( @@ -492,7 +491,7 @@ class IPsecUtil: ), dscp=int(IpDscp.IP_API_DSCP_CS0), ), - protocol=int(IPsecProto.IPSEC_API_PROTO_ESP), + protocol=IPsecProto.ESP, udp_src_port=IPSEC_UDP_PORT_DEFAULT, udp_dst_port=IPSEC_UDP_PORT_DEFAULT, anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT, @@ -507,9 +506,9 @@ class IPsecUtil: n_entries: int, sad_id: int, spi: int, - crypto_alg: CryptoAlg, - crypto_key: str, - integ_alg: Optional[IntegAlg] = None, + crypto_alg: CryptoAlg.InputType = None, + crypto_key: str = "", + integ_alg: IntegAlg.InputType = None, integ_key: str = "", tunnel_src: Optional[str] = None, tunnel_dst: Optional[str] = None, @@ -537,14 +536,16 @@ class IPsecUtil: :type n_entries: int :type sad_id: int :type spi: int - :type crypto_alg: CryptoAlg + :type crypto_alg: CryptoAlg.InputType :type crypto_key: str - :type integ_alg: Optional[IntegAlg] + :type integ_alg: IntegAlg.InputType :type integ_key: str :type tunnel_src: Optional[str] :type tunnel_dst: Optional[str] :type tunnel_addr_incr: bool """ + crypto_alg = get_enum_instance(CryptoAlg, crypto_alg) + integ_alg = get_enum_instance(IntegAlg, integ_alg) if isinstance(crypto_key, str): crypto_key = crypto_key.encode(encoding="utf-8") if isinstance(integ_key, str): @@ -585,7 +586,7 @@ class IPsecUtil: spi=int(spi), crypto_algorithm=crypto_alg.alg_int_repr, crypto_key=ckey, - integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0, + integrity_algorithm=integ_alg.alg_int_repr, integrity_key=ikey, flags=flags, tunnel=dict( @@ -597,7 +598,7 @@ class IPsecUtil: ), dscp=int(IpDscp.IP_API_DSCP_CS0), ), - protocol=int(IPsecProto.IPSEC_API_PROTO_ESP), + protocol=IPsecProto.ESP, udp_src_port=IPSEC_UDP_PORT_DEFAULT, udp_dst_port=IPSEC_UDP_PORT_DEFAULT, anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT, @@ -774,7 +775,7 @@ class IPsecUtil: entry_amount: int, local_addr_range: Union[str, IPv4Address, IPv6Address], remote_addr_range: Union[str, IPv4Address, IPv6Address], - action: PolicyAction = PolicyAction.BYPASS, + action: IpsecSpdAction.InputType = IpsecSpdAction.BYPASS, inbound: bool = False, bidirectional: bool = True, ) -> None: @@ -801,7 +802,7 @@ class IPsecUtil: :param remote_addr_range: Matching remote address range in direction 1 in format IP/prefix or IP/mask. If no mask is provided, it's considered to be /32. - :param action: Policy action. + :param action: IPsec SPD action. :param inbound: If True policy is for inbound traffic, otherwise outbound. :param bidirectional: When True, will create SPDs in both directions @@ -814,14 +815,16 @@ class IPsecUtil: Union[str, IPv4Address, IPv6Address] :type remote_addr_range: Union[str, IPv4Address, IPv6Address] - :type action: PolicyAction + :type action: IpsecSpdAction.InputType :type inbound: bool :type bidirectional: bool - :raises NotImplementedError: When the action is PolicyAction.PROTECT. + :raises NotImplementedError: When the action is IpsecSpdAction.PROTECT. """ - - if action == PolicyAction.PROTECT: - raise NotImplementedError("Policy action PROTECT is not supported.") + action = get_enum_instance(IpsecSpdAction, action) + if action == IpsecSpdAction.PROTECT: + raise NotImplementedError( + "IPsec SPD action PROTECT is not supported." + ) spd_id_dir1 = 1 spd_id_dir2 = 2 @@ -913,10 +916,10 @@ class IPsecUtil: executor: PapiSocketExecutor, spd_id: int, priority: int, - action: PolicyAction, + action: IpsecSpdAction.InputType, inbound: bool = True, sa_id: Optional[int] = None, - proto: Optional[int] = None, + proto: IPsecProto.InputType = None, laddr_range: Optional[str] = None, raddr_range: Optional[str] = None, lport_range: Optional[str] = None, @@ -932,10 +935,10 @@ class IPsecUtil: :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. + :param action: IPsec SPD action. :param inbound: If True policy is for inbound traffic, otherwise outbound. - :param sa_id: SAD entry ID for action PolicyAction.PROTECT. + :param sa_id: SAD entry ID for action IpsecSpdAction.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, @@ -952,16 +955,18 @@ class IPsecUtil: :type executor: PapiSocketExecutor :type spd_id: int :type priority: int - :type action: PolicyAction + :type action: IpsecSpdAction.InputType :type inbound: bool :type sa_id: Optional[int] - :type proto: Optional[int] + :type proto: IPsecProto.InputType :type laddr_range: Optional[str] :type raddr_range: Optional[str] :type lport_range: Optional[str] :type rport_range: Optional[str] :type is_ipv6: bool """ + action = get_enum_instance(IpsecSpdAction, action) + proto = get_enum_instance(IPsecProto, proto) if laddr_range is None: laddr_range = "::/0" if is_ipv6 else "0.0.0.0/0" @@ -979,7 +984,7 @@ class IPsecUtil: is_outbound=not inbound, sa_id=int(sa_id) if sa_id else 0, policy=int(action), - protocol=255 if proto is None else int(proto), + protocol=proto, remote_address_start=IPAddress.create_ip_address_object( remote_net.network_address ), @@ -1013,10 +1018,10 @@ class IPsecUtil: node: dict, spd_id: int, priority: int, - action: PolicyAction, + action: IpsecSpdAction.InputType, inbound: bool = True, sa_id: Optional[int] = None, - proto: Optional[int] = None, + proto: IPsecProto.InputType = None, laddr_range: Optional[str] = None, raddr_range: Optional[str] = None, lport_range: Optional[str] = None, @@ -1028,10 +1033,10 @@ class IPsecUtil: :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 action: IPsec SPD action. :param inbound: If True policy is for inbound traffic, otherwise outbound. - :param sa_id: SAD entry ID for action PolicyAction.PROTECT. + :param sa_id: SAD entry ID for action IpsecSpdAction.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, @@ -1048,16 +1053,18 @@ class IPsecUtil: :type node: dict :type spd_id: int :type priority: int - :type action: PolicyAction + :type action: IpsecSpdAction.InputType :type inbound: bool :type sa_id: Optional[int] - :type proto: Optional[int] + :type proto: IPsecProto.InputType :type laddr_range: Optional[str] :type raddr_range: Optional[str] :type lport_range: Optional[str] :type rport_range: Optional[str] :type is_ipv6: bool """ + action = get_enum_instance(IpsecSpdAction, action) + proto = get_enum_instance(IPsecProto, proto) err_msg = ( "Failed to add entry to Security Policy Database" f" {spd_id} on host {node['host']}" @@ -1085,10 +1092,10 @@ class IPsecUtil: n_entries: int, spd_id: int, priority: Optional[ObjIncrement], - action: PolicyAction, + action: IpsecSpdAction.InputType, inbound: bool, sa_id: Optional[ObjIncrement] = None, - proto: Optional[int] = None, + proto: IPsecProto.InputType = None, laddr_range: Optional[NetworkIncrement] = None, raddr_range: Optional[NetworkIncrement] = None, lport_range: Optional[str] = None, @@ -1101,10 +1108,10 @@ class IPsecUtil: :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 action: Policy action. + :param action: IPsec SPD action. :param inbound: If True policy is for inbound traffic, otherwise outbound. - :param sa_id: SAD entry ID for action PolicyAction.PROTECT. + :param sa_id: SAD entry ID for action IpsecSpdAction.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, @@ -1122,16 +1129,18 @@ class IPsecUtil: :type n_entries: int :type spd_id: int :type priority: Optional[ObjIncrement] - :type action: PolicyAction + :type action: IpsecSpdAction.InputType :type inbound: bool :type sa_id: Optional[ObjIncrement] - :type proto: Optional[int] + :type proto: IPsecProto.InputType :type laddr_range: Optional[NetworkIncrement] :type raddr_range: Optional[NetworkIncrement] :type lport_range: Optional[str] :type rport_range: Optional[str] :type is_ipv6: bool """ + action = get_enum_instance(IpsecSpdAction, action) + proto = get_enum_instance(IPsecProto, proto) if laddr_range is None: laddr_range = "::/0" if is_ipv6 else "0.0.0.0/0" laddr_range = NetworkIncrement(ip_network(laddr_range), 0) @@ -1253,12 +1262,14 @@ class IPsecUtil: if1_key: str, if2_key: str, n_tunnels: int, - crypto_alg: CryptoAlg, - integ_alg: Optional[IntegAlg], + crypto_alg: CryptoAlg.InputType, + integ_alg: IntegAlg.InputType, raddr_ip2: Union[IPv4Address, IPv6Address], addr_incr: int, spi_d: dict, existing_tunnels: int = 0, + udp_encap: bool = False, + anti_replay: bool = False, ) -> Tuple[List[bytes], List[bytes]]: """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI. @@ -1280,20 +1291,26 @@ class IPsecUtil: :param addr_incr: IP / IPv6 address incremental step. :param existing_tunnels: Number of tunnel interfaces before creation. Useful mainly for reconf tests. Default 0. + :param udp_encap: Whether to apply UDP_ENCAP flag. + :param anti_replay: Whether to apply USE_ANTI_REPLAY flag. :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 crypto_alg: CryptoAlg.InputType + :type integ_alg: IntegAlg.InputType :type raddr_ip2: Union[IPv4Address, IPv6Address] :type addr_incr: int :type spi_d: dict :type existing_tunnels: int + :type udp_encap: bool + :type anti_replay: bool :returns: Generated ckeys and ikeys. :rtype: List[bytes], List[bytes] """ + crypto_alg = get_enum_instance(CryptoAlg, crypto_alg) + integ_alg = get_enum_instance(IntegAlg, integ_alg) if not existing_tunnels: loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi( nodes, tun_ips, if1_key, if2_key @@ -1362,13 +1379,17 @@ class IPsecUtil: c_key = dict(length=0, data=None) i_key = dict(length=0, data=None) common_flags = IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE + if udp_encap: + common_flags |= IPsecSadFlags.IPSEC_API_SAD_FLAG_UDP_ENCAP + if anti_replay: + common_flags |= IPsecSadFlags.IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY sad_entry = dict( sad_id=None, spi=None, - protocol=int(IPsecProto.IPSEC_API_PROTO_ESP), + protocol=IPsecProto.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_algorithm=integ_alg.alg_int_repr, integrity_key=i_key, flags=common_flags, tunnel=dict( @@ -1387,12 +1408,8 @@ class IPsecUtil: ) 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)) - ) - ikeys.append( - gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) - ) + ckeys.append(gen_key(crypto_alg.key_len)) + ikeys.append(gen_key(integ_alg.key_len)) # SAD entry for outband / tx path sad_entry["sad_id"] = i sad_entry["spi"] = spi_d["spi_1"] + i @@ -1497,14 +1514,16 @@ class IPsecUtil: tun_ips: dict, if2_key: str, n_tunnels: int, - crypto_alg: CryptoAlg, + crypto_alg: CryptoAlg.InputType, ckeys: Sequence[bytes], - integ_alg: Optional[IntegAlg], + integ_alg: IntegAlg.InputType, ikeys: Sequence[bytes], raddr_ip1: Union[IPv4Address, IPv6Address], addr_incr: int, spi_d: dict, existing_tunnels: int = 0, + udp_encap: bool = False, + anti_replay: bool = False, ) -> None: """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI. @@ -1528,19 +1547,25 @@ class IPsecUtil: :param addr_incr: IP / IPv6 address incremental step. :param existing_tunnels: Number of tunnel interfaces before creation. Useful mainly for reconf tests. Default 0. + :param udp_encap: Whether to apply UDP_ENCAP flag. + :param anti_replay: Whether to apply USE_ANTI_REPLAY flag. :type nodes: dict :type tun_ips: dict :type if2_key: str :type n_tunnels: int - :type crypto_alg: CryptoAlg + :type crypto_alg: CryptoAlg.InputType :type ckeys: Sequence[bytes] - :type integ_alg: Optional[IntegAlg] + :type integ_alg: IntegAlg.InputType :type ikeys: Sequence[bytes] :type raddr_ip1: Union[IPv4Address, IPv6Address] :type addr_incr: int :type spi_d: dict :type existing_tunnels: int + :type udp_encap: bool + :type anti_replay: bool """ + crypto_alg = get_enum_instance(CryptoAlg, crypto_alg) + integ_alg = get_enum_instance(IntegAlg, integ_alg) with PapiSocketExecutor(nodes["DUT2"], is_async=True) as papi_exec: if not existing_tunnels: # Set IP address on VPP node 2 interface @@ -1602,13 +1627,17 @@ class IPsecUtil: c_key = dict(length=0, data=None) i_key = dict(length=0, data=None) common_flags = IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE + if udp_encap: + common_flags |= IPsecSadFlags.IPSEC_API_SAD_FLAG_UDP_ENCAP + if anti_replay: + common_flags |= IPsecSadFlags.IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY sad_entry = dict( sad_id=None, spi=None, - protocol=int(IPsecProto.IPSEC_API_PROTO_ESP), + protocol=IPsecProto.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_algorithm=integ_alg.alg_int_repr, integrity_key=i_key, flags=common_flags, tunnel=dict( @@ -1627,12 +1656,8 @@ class IPsecUtil: ) 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)) - ) - ikeys.append( - gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) - ) + ckeys.append(gen_key(crypto_alg.key_len)) + ikeys.append(gen_key(integ_alg.key_len)) # SAD entry for outband / tx path sad_entry["sad_id"] = 100000 + i sad_entry["spi"] = spi_d["spi_2"] + i @@ -1749,12 +1774,14 @@ class IPsecUtil: if1_key: str, if2_key: str, n_tunnels: int, - crypto_alg: CryptoAlg, - integ_alg: Optional[IntegAlg], + crypto_alg: CryptoAlg.InputType, + integ_alg: IntegAlg.InputType, raddr_ip1: str, raddr_ip2: str, raddr_range: int, existing_tunnels: int = 0, + udp_encap: bool = False, + anti_replay: bool = False, return_keys: bool = False, ) -> Optional[Tuple[List[bytes], List[bytes], int, int]]: """Create multiple IPsec tunnel interfaces between two VPP nodes. @@ -1784,22 +1811,28 @@ class IPsecUtil: :param existing_tunnels: Number of tunnel interfaces before creation. Useful mainly for reconf tests. Default 0. :param return_keys: Whether generated keys should be returned. + :param udp_encap: Whether to apply UDP_ENCAP flag. + :param anti_replay: Whether to apply USE_ANTI_REPLAY flag. :type nodes: dict :type tun_if1_ip_addr: str :type tun_if2_ip_addr: str :type if1_key: str :type if2_key: str :type n_tunnels: int - :type crypto_alg: CryptoAlg - :type integ_alg: Optional[IntegAlg] + :type crypto_alg: CryptoAlg.InputType + :type integ_alg: IntegAlg.InputType :type raddr_ip1: str :type raddr_ip2: str :type raddr_range: int :type existing_tunnels: int :type return_keys: bool + :type udp_encap: bool + :type anti_replay: bool :returns: Ckeys, ikeys, spi_1, spi_2. :rtype: Optional[Tuple[List[bytes], List[bytes], int, int]] """ + crypto_alg = get_enum_instance(CryptoAlg, crypto_alg) + integ_alg = get_enum_instance(IntegAlg, integ_alg) n_tunnels = int(n_tunnels) existing_tunnels = int(existing_tunnels) spi_d = dict(spi_1=100000, spi_2=200000) @@ -1826,6 +1859,8 @@ class IPsecUtil: addr_incr, spi_d, existing_tunnels, + udp_encap, + anti_replay, ) if "DUT2" in nodes.keys(): IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi( @@ -1841,6 +1876,8 @@ class IPsecUtil: addr_incr, spi_d, existing_tunnels, + udp_encap, + anti_replay, ) if return_keys: @@ -1896,8 +1933,8 @@ class IPsecUtil: interface1: Union[str, int], interface2: Union[str, int], n_tunnels: int, - crypto_alg: CryptoAlg, - integ_alg: Optional[IntegAlg], + crypto_alg: CryptoAlg.InputType, + integ_alg: IntegAlg.InputType, tunnel_ip1: str, tunnel_ip2: str, raddr_ip1: str, @@ -1927,8 +1964,8 @@ class IPsecUtil: :type interface1: Union[str, int] :type interface2: Union[str, int] :type n_tunnels: int - :type crypto_alg: CryptoAlg - :type integ_alg: Optional[IntegAlg] + :type crypto_alg: CryptoAlg.InputType + :type integ_alg: IntegAlg.InputType :type tunnel_ip1: str :type tunnel_ip2: str :type raddr_ip1: str @@ -1936,6 +1973,9 @@ class IPsecUtil: :type raddr_range: int :type tunnel_addr_incr: bool """ + crypto_alg = get_enum_instance(CryptoAlg, crypto_alg) + integ_alg = get_enum_instance(IntegAlg, integ_alg) + spd_id = 1 p_hi = 100 p_lo = 10 @@ -1944,15 +1984,8 @@ class IPsecUtil: spi_1 = 300000 spi_2 = 400000 - crypto_key = gen_key( - IPsecUtil.get_crypto_alg_key_len(crypto_alg) - ).decode() - integ_key = ( - gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)).decode() - if integ_alg - else "" - ) - + crypto_key = gen_key(crypto_alg.key_len).decode() + integ_key = gen_key(integ_alg.key_len).decode() rmac = ( Topology.get_interface_mac(nodes["DUT2"], interface2) if "DUT2" in nodes.keys() @@ -1989,9 +2022,9 @@ class IPsecUtil: nodes["DUT1"], spd_id, p_hi, - PolicyAction.BYPASS, + IpsecSpdAction.BYPASS, inbound=False, - proto=50, + proto=IPsecProto.ESP, laddr_range=dut1_local_outbound_range, raddr_range=dut1_remote_outbound_range, ) @@ -1999,9 +2032,9 @@ class IPsecUtil: nodes["DUT1"], spd_id, p_hi, - PolicyAction.BYPASS, + IpsecSpdAction.BYPASS, inbound=True, - proto=50, + proto=IPsecProto.ESP, laddr_range=dut1_remote_outbound_range, raddr_range=dut1_local_outbound_range, ) @@ -2025,7 +2058,7 @@ class IPsecUtil: n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0), - action=PolicyAction.PROTECT, + action=IpsecSpdAction.PROTECT, inbound=False, sa_id=ObjIncrement(sa_id_1, 1), raddr_range=NetworkIncrement(ip_network(raddr_ip2)), @@ -2049,7 +2082,7 @@ class IPsecUtil: n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0), - action=PolicyAction.PROTECT, + action=IpsecSpdAction.PROTECT, inbound=True, sa_id=ObjIncrement(sa_id_2, 1), raddr_range=NetworkIncrement(ip_network(raddr_ip1)), @@ -2082,9 +2115,9 @@ class IPsecUtil: nodes["DUT2"], spd_id, p_hi, - PolicyAction.BYPASS, + IpsecSpdAction.BYPASS, inbound=False, - proto=50, + proto=IPsecProto.ESP, laddr_range=dut2_remote_outbound_range, raddr_range=dut2_local_outbound_range, ) @@ -2092,9 +2125,9 @@ class IPsecUtil: nodes["DUT2"], spd_id, p_hi, - PolicyAction.BYPASS, + IpsecSpdAction.BYPASS, inbound=True, - proto=50, + proto=IPsecProto.ESP, laddr_range=dut2_local_outbound_range, raddr_range=dut2_remote_outbound_range, ) @@ -2117,7 +2150,7 @@ class IPsecUtil: n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0), - action=PolicyAction.PROTECT, + action=IpsecSpdAction.PROTECT, inbound=True, sa_id=ObjIncrement(sa_id_1, 1), raddr_range=NetworkIncrement(ip_network(raddr_ip2)), @@ -2141,7 +2174,7 @@ class IPsecUtil: n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0), - action=PolicyAction.PROTECT, + action=IpsecSpdAction.PROTECT, inbound=False, sa_id=ObjIncrement(sa_id_2, 1), raddr_range=NetworkIncrement(ip_network(raddr_ip1)), @@ -2168,7 +2201,10 @@ class IPsecUtil: @staticmethod def vpp_ipsec_flow_enable_rss( - node: dict, proto: str, rss_type: str, function: str = "default" + node: dict, + proto: str = "IPSEC_ESP", + rss_type: str = "esp", + function: str = "default", ) -> int: """Ipsec flow enable rss action. @@ -2176,14 +2212,19 @@ class IPsecUtil: :param proto: The flow protocol. :param rss_type: RSS type. :param function: RSS function. - :type node: dict - :type proto: str + :type proto: IPsecProto.InputType :type rss_type: str :type function: str :returns: flow_index. :rtype: int """ + # The proto argument does not correspond to IPsecProto. + # The allowed values come from src/vnet/ip/protocols.def + # and we do not have a good enum for that yet. + # FlowUtil.FlowType and FlowUtil.FlowProto are close, + # but not exactly the same. + # 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" diff --git a/resources/libraries/python/KubernetesUtils.py b/resources/libraries/python/KubernetesUtils.py index 9ded0e8b9e..a58c337a18 100644 --- a/resources/libraries/python/KubernetesUtils.py +++ b/resources/libraries/python/KubernetesUtils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # 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: diff --git a/resources/libraries/python/QemuManager.py b/resources/libraries/python/QemuManager.py index 259b4c6981..78c88ddf3c 100644 --- a/resources/libraries/python/QemuManager.py +++ b/resources/libraries/python/QemuManager.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # 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: @@ -165,18 +165,18 @@ class QemuManager: vif1_mac=kwargs[u"vif1_mac"], vif2_mac=kwargs[u"vif2_mac"], queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) self.machines[name].add_vhost_user_if( f"/run/vpp/sock-{qemu_id}-1", - jumbo_frames=kwargs[u"jumbo"], + jumbo=kwargs[u"jumbo"], queues=kwargs[u"queues"], queue_size=kwargs[u"perf_qemu_qsz"], virtio_feature_mask=virtio_feature_mask ) self.machines[name].add_vhost_user_if( f"/run/vpp/sock-{qemu_id}-2", - jumbo_frames=kwargs[u"jumbo"], + jumbo=kwargs[u"jumbo"], queues=kwargs[u"queues"], queue_size=kwargs[u"perf_qemu_qsz"], virtio_feature_mask=virtio_feature_mask @@ -215,7 +215,7 @@ class QemuManager: arpip1=u"1.1.1.1", arpif1=u"avf-0/0/7/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) else: self.machines[name].configure_kernelvm_vnf( @@ -231,7 +231,7 @@ class QemuManager: arpip1=u"3.3.3.1", arpif1=u"avf-0/0/6/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) self.machines[name].add_vfio_pci_if( pci=Topology.get_interface_pci_addr( @@ -275,7 +275,7 @@ class QemuManager: arpip1=u"1.1.1.1", arpif1=u"avf-0/0/7/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) else: self.machines[name].configure_kernelvm_vnf( @@ -291,7 +291,7 @@ class QemuManager: arpip1=u"3.3.3.1", arpif1=u"avf-0/0/6/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) self.machines[name].add_vfio_pci_if( pci=Topology.get_interface_pci_addr( @@ -335,7 +335,7 @@ class QemuManager: arpip1=u"1.1.1.1", arpif1=u"avf-0/0/7/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) else: self.machines[name].configure_kernelvm_vnf( @@ -351,7 +351,7 @@ class QemuManager: arpip1=u"3.3.3.1", arpif1=u"avf-0/0/6/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) self.machines[name].add_vfio_pci_if( pci=Topology.get_interface_pci_addr( @@ -395,7 +395,7 @@ class QemuManager: arpip1=u"1.1.1.1", arpif1=u"avf-0/0/7/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) else: self.machines[name].configure_kernelvm_vnf( @@ -411,7 +411,7 @@ class QemuManager: arpip1=u"3.3.3.1", arpif1=u"avf-0/0/6/0", queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) self.machines[name].add_vfio_pci_if( pci=Topology.get_interface_pci_addr( @@ -445,13 +445,13 @@ class QemuManager: self.machines[name].add_kernelvm_params() self.machines[name].configure_kernelvm_vnf( queues=kwargs[u"queues"], - jumbo_frames=kwargs[u"jumbo"] + jumbo=kwargs[u"jumbo"] ) self.machines[name].add_net_user() self.machines[name].add_vhost_user_if( f"/run/vpp/sock-{qemu_id}-1", server=False, - jumbo_frames=kwargs[u"jumbo"], + jumbo=kwargs[u"jumbo"], queues=kwargs[u"queues"], queue_size=kwargs[u"perf_qemu_qsz"], virtio_feature_mask=virtio_feature_mask diff --git a/resources/libraries/python/QemuUtils.py b/resources/libraries/python/QemuUtils.py index 2df89ee87c..8dac06001c 100644 --- a/resources/libraries/python/QemuUtils.py +++ b/resources/libraries/python/QemuUtils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2023 Cisco and/or its affiliates. +# Copyright (c) 2024-2024 Cisco and/or its affiliates. # 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: @@ -236,25 +236,25 @@ class QemuUtils: ) def add_vhost_user_if( - self, socket, server=True, jumbo_frames=False, queue_size=None, + self, socket, server=True, jumbo=False, queue_size=None, queues=1, virtio_feature_mask=None): """Add Vhost-user interface. :param socket: Path of the unix socket. :param server: If True the socket shall be a listening socket. - :param jumbo_frames: Set True if jumbo frames are used in the test. + :param jumbo: Set True if jumbo frames are used in the test. :param queue_size: Vring queue size. :param queues: Number of queues. :param virtio_feature_mask: Mask of virtio features to be enabled. :type socket: str :type server: bool - :type jumbo_frames: bool + :type jumbo: bool :type queue_size: int :type queues: int :type virtio_feature_mask: int """ self._nic_id += 1 - if jumbo_frames: + if jumbo: logger.debug(u"Jumbo frames temporarily disabled!") self._params.add_with_value( u"chardev", f"socket,id=char{self._nic_id}," @@ -342,7 +342,7 @@ class QemuUtils: vpp_config.add_dpdk_dev(u"0000:00:06.0", u"0000:00:07.0") vpp_config.add_dpdk_dev_default_rxq(kwargs[u"queues"]) vpp_config.add_dpdk_log_level(u"debug") - if not kwargs[u"jumbo_frames"]: + if not kwargs[u"jumbo"]: vpp_config.add_dpdk_no_multi_seg() vpp_config.add_dpdk_no_tx_checksum_offload() if "ipsec" in self._opt.get(u'vnf'): @@ -371,7 +371,7 @@ class QemuUtils: :param kwargs: Key-value pairs to construct command line parameters. :type kwargs: dict """ - pmd_max_pkt_len = u"9200" if kwargs[u"jumbo_frames"] else u"1518" + pmd_max_pkt_len = u"9200" if kwargs[u"jumbo"] else u"1518" testpmd_cmd = DpdkUtil.get_testpmd_cmdline( eal_corelist=f"0-{self._opt.get(u'smp') - 1}", eal_driver=False, @@ -398,7 +398,7 @@ class QemuUtils: :param kwargs: Key-value pairs to construct command line parameters. :type kwargs: dict """ - pmd_max_pkt_len = u"9200" if kwargs[u"jumbo_frames"] else u"1518" + pmd_max_pkt_len = u"9200" if kwargs[u"jumbo"] else u"1518" testpmd_cmd = DpdkUtil.get_testpmd_cmdline( eal_corelist=f"0-{self._opt.get(u'smp') - 1}", eal_driver=False, diff --git a/resources/libraries/python/VppConfigGenerator.py b/resources/libraries/python/VppConfigGenerator.py index 4191c0eed2..fb3df2fc16 100644 --- a/resources/libraries/python/VppConfigGenerator.py +++ b/resources/libraries/python/VppConfigGenerator.py @@ -296,6 +296,15 @@ class VppConfigGenerator: path = ["dpdk", cryptodev_config] self.add_config_item(self._nodeconfig, "", path) + def add_dpdk_dev_default_devargs(self, value): + """Add DPDK dev default devargs configuration. + + :param value: DPDK devargs to pass to interface. + :type value: str + """ + path = ["dpdk", "dev default", "devargs"] + self.add_config_item(self._nodeconfig, value, path) + def add_dpdk_dev_default_rxq(self, value): """Add DPDK dev default rxq configuration. @@ -334,7 +343,7 @@ class VppConfigGenerator: def add_dpdk_dev_default_tso(self): """Add DPDK dev default tso configuration.""" - path = [u"dpdk", u"dev default", u"tso"] + path = ["dpdk", "dev default", "tso"] self.add_config_item(self._nodeconfig, "on", path) def add_dpdk_log_level(self, value): @@ -382,7 +391,7 @@ class VppConfigGenerator: def add_dpdk_enable_tcp_udp_checksum(self): """Add DPDK enable-tcp-udp-checksum configuration.""" - path = [u"dpdk", u"enable-tcp-udp-checksum"] + path = ["dpdk", "enable-tcp-udp-checksum"] self.add_config_item(self._nodeconfig, u"", path) def add_cpu_main_core(self, value): @@ -477,7 +486,7 @@ class VppConfigGenerator: :param value: "on" to enable spd fast path. :type value: str """ - path = [u"ipsec", u"ipv4-inbound-spd-fast-path"] + path = ["ipsec", "ipv4-inbound-spd-fast-path"] self.add_config_item(self._nodeconfig, value, path) def add_ipsec_spd_fast_path_ipv4_outbound(self, value): @@ -542,6 +551,11 @@ class VppConfigGenerator: path = ["dpdk", "no-multi-seg"] self.add_config_item(self._nodeconfig, "", path) + def add_dpdk_enable_tcp_udp_checksum(self): + """Add DPDK enable-tcp-udp-checksum configuration.""" + path = ["dpdk", "enable-tcp-udp-checksum"] + self.add_config_item(self._nodeconfig, "", path) + def add_dpdk_no_tx_checksum_offload(self): """Add DPDK no-tx-checksum-offload configuration.""" path = ["dpdk", "no-tx-checksum-offload"] @@ -599,8 +613,8 @@ class VppConfigGenerator: def add_tcp_tso(self): """Add TCP tso configuration.""" - path = [u"tcp", u"tso"] - self.add_config_item(self._nodeconfig, u"", path) + path = ["tcp", "tso"] + self.add_config_item(self._nodeconfig, "", path) def add_session_enable(self): """Add session enable.""" @@ -794,3 +808,44 @@ class VppInitConfig: vpp_config.add_ip6_hash_buckets(2000000) vpp_config.add_ip6_heap_size("4G") vpp_config.apply_config() + + @staticmethod + def create_vpp_startup_configuration_container(node, cpuset_cpus=None): + """Create base startup configuration of VPP on container. + + :param node: Node in the topology. + :param cpuset_cpus: List of CPU cores to allocate. + :type node: dict + :type cpuset_cpus: list. + :returns: Base VPP startup configuration for container. + :rtype: VppConfigGenerator + """ + huge_size = Constants.DEFAULT_HUGEPAGE_SIZE + + vpp_config = VppConfigGenerator() + vpp_config.set_node(node) + vpp_config.add_unix_log() + vpp_config.add_unix_cli_listen() + vpp_config.add_unix_cli_no_pager() + vpp_config.add_unix_exec("/tmp/running.exec") + vpp_config.add_socksvr(socket=Constants.SOCKSVR_PATH) + if cpuset_cpus: + # We will pop the first core from the list to be a main core + vpp_config.add_cpu_main_core(str(cpuset_cpus.pop(0))) + # If more cores in the list, the rest will be used as workers. + corelist_workers = ",".join(str(cpu) for cpu in cpuset_cpus) + vpp_config.add_cpu_corelist_workers(corelist_workers) + vpp_config.add_buffers_per_numa(215040) + vpp_config.add_plugin("disable", "default") + vpp_config.add_plugin("enable", "memif_plugin.so") + vpp_config.add_plugin("enable", "perfmon_plugin.so") + vpp_config.add_main_heap_size("2G") + vpp_config.add_main_heap_page_size(huge_size) + vpp_config.add_default_hugepage_size(huge_size) + vpp_config.add_statseg_size("2G") + vpp_config.add_statseg_page_size(huge_size) + vpp_config.add_statseg_per_node_counters("on") + vpp_config.add_ip6_hash_buckets(2000000) + vpp_config.add_ip6_heap_size("4G") + + return vpp_config diff --git a/resources/libraries/python/autogen/Regenerator.py b/resources/libraries/python/autogen/Regenerator.py index 8d593fecca..ce8f21d4b6 100644 --- a/resources/libraries/python/autogen/Regenerator.py +++ b/resources/libraries/python/autogen/Regenerator.py @@ -178,9 +178,6 @@ def add_default_testcases( if u"reassembly" in suite_id: if kwargs[u"frame_size"] != 1518: emit = False - else: - if kwargs[u"frame_size"] not in MIN_FRAME_SIZE_VALUES: - emit = False kwargs.update({'phy_cores': kwas['phy_cores']*core_scale}) diff --git a/resources/libraries/python/enum_util.py b/resources/libraries/python/enum_util.py new file mode 100644 index 0000000000..f721936ee4 --- /dev/null +++ b/resources/libraries/python/enum_util.py @@ -0,0 +1,70 @@ +# Copyright (c) 2024 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utility functions for handling VPP API enum values from Robot.""" + + +from enum import Enum, IntEnum +from typing import Type, Union + + +# The return type is enum_class, but it is hard to explain that to pylint. +def get_enum_instance( + enum_class: Type[Enum], value: Union[Enum, str, int, None] +) -> Enum: + """Return an enum instance matching the string name. + + In Robot, it is not convenient to construct Enum instances, + most values defined in Robot are strings. + + This helper function can be used in Python L1 keywords + to convert string into the corresponding Enum instance. + Aliases are also recognized. + + As a common shortcut, value is returned it it already is an instance. + + Another convenience: None or empty string is processed as "NONE". + + As an added benefit, support various Robot-like niceties, + like lower case, or dash or space instead of underscore. + Also strip the identifiers, this is mostly due to "3DES". + Enum instance cannot start with a number, so "_3DES" + strip is needed. + + If the class is a subclass of IntEnum, int values + and (string) values convertable to int are also accepted as input. + + :param enum_class: Class object instance of which should be returned. + :param value: String or any other recognized form of an enum instance. + :type enum_class: Type[Enum] + :type value: Union[enum_class, str, int, None] + :returns: The matching instance, if found. + :rtype: enum_class + :raises: ValueError if no matching instance is found. + """ + if issubclass(enum_class, IntEnum): + try: + int_value = int(value) + return enum_class(int_value) + except (TypeError, ValueError): + pass + if isinstance(value, enum_class): + return value + if not value: + value = "NONE" + normalized_name = str(value).upper().replace("-", " ").replace("_", " ") + members = enum_class.__members__ # Includes aliases, useful for NONE. + for member_name in members: + if normalized_name.strip() == member_name.replace("_", " ").strip(): + return members[member_name] + msg = f"Enum class {enum_class} does not have value {normalized_name!r}" + raise ValueError(msg) |