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