X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FIPsecUtil.py;h=d7f21361384c083ff964c9a7e19a1bf066fedca5;hb=5302eeabb757ccf710568a66b8f6435c1894cd4d;hp=2efc70eab65d7172943a6acc0ec7dcec165689a2;hpb=000ce799bfe473489bbe95e8b00a460270e1ff0b;p=csit.git diff --git a/resources/libraries/python/IPsecUtil.py b/resources/libraries/python/IPsecUtil.py index 2efc70eab6..d7f2136138 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: @@ -34,6 +34,7 @@ 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 @@ -437,7 +438,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 +449,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 +487,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 +499,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,8 +512,11 @@ class IPsecUtil: src_addr = u"" dst_addr = u"" - addr_incr = 1 << (128 - 96) if src_addr.version == 6 \ - else 1 << (32 - 24) + if tunnel_addr_incr: + addr_incr = 1 << (128 - 96) if src_addr.version == 6 \ + else 1 << (32 - 24) + else: + addr_incr = 0 if int(n_entries) > 10: tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script" @@ -551,7 +559,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 +571,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,6 +629,7 @@ 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) @@ -626,11 +639,15 @@ class IPsecUtil: with open(tmp_filename, 'w') as tmp_file: if_name = Topology.get_interface_name(node, interface) for i in range(n_tunnels): + tunnel_dst_addr = tunnel_dst + i * addr_incr 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" + f"{tunnel_dst_prefix} " \ + f"via {tunnel_dst_addr} {if_name}\n" \ + f"exec ip route add {tunnel_dst_addr}/" \ + f"{tunnel_dst_prefix} " \ + f"via {tunnel_dst_addr} {if_name}\n" if dst_mac: conf = f"{conf}exec set ip neighbor {if_name} " \ f"{tunnel_dst + i * addr_incr} {dst_mac}\n" @@ -672,22 +689,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) + + 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 +803,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 @@ -1004,7 +1030,7 @@ class IPsecUtil: 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): + for _ 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 '' @@ -1031,7 +1057,7 @@ class IPsecUtil: os.remove(tmp_filename) return - for i in range(n_entries): + for _ 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, @@ -1383,7 +1409,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 +1465,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 +1483,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 +1649,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 +1666,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 +1704,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 +1717,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)) @@ -2088,7 +2110,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 +2128,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 +2141,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 @@ -2124,6 +2150,10 @@ class IPsecUtil: sa_id_2 = 200000 spi_1 = 300000 spi_2 = 400000 + dut1_local_outbound_range = ip_network(f"{tunnel_ip1}/8", False).\ + with_prefixlen + dut1_remote_outbound_range = ip_network(f"{tunnel_ip2}/8", False).\ + with_prefixlen crypto_key = gen_key( IPsecUtil.get_crypto_alg_key_len(crypto_alg) @@ -2143,16 +2173,18 @@ class IPsecUtil: 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" + 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=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8" + 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 +2196,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 +2206,32 @@ 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" + inbound=False, proto=50, laddr_range=dut1_remote_outbound_range, + raddr_range=dut1_local_outbound_range ) 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" + inbound=True, proto=50, laddr_range=dut1_local_outbound_range, + raddr_range=dut1_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 +2239,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 +2267,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)