1 # Copyright (c) 2024 Cisco and/or its affiliates.
2 # Copyright (c) 2024 PANTHEON.tech s.r.o.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """IPsec utilities library."""
17 from enum import Enum, IntEnum
18 from io import open, TextIOWrapper
19 from ipaddress import ip_network, ip_address, IPv4Address, IPv6Address
20 from random import choice
21 from string import ascii_letters
22 from typing import Iterable, List, Optional, Sequence, Tuple, Union
24 from robot.libraries.BuiltIn import BuiltIn
26 from resources.libraries.python.Constants import Constants
27 from resources.libraries.python.IncrementUtil import ObjIncrement
28 from resources.libraries.python.InterfaceUtil import (
32 from resources.libraries.python.IPAddress import IPAddress
33 from resources.libraries.python.IPUtil import (
39 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
40 from resources.libraries.python.ssh import scp_node
41 from resources.libraries.python.topology import Topology, NodeType
42 from resources.libraries.python.VPPUtil import VPPUtil
43 from resources.libraries.python.FlowUtil import FlowUtil
46 IPSEC_UDP_PORT_DEFAULT = 4500
47 IPSEC_REPLAY_WINDOW_DEFAULT = 64
50 def gen_key(length: int) -> bytes:
51 """Generate random string as a key.
53 :param length: Length of generated payload.
55 :returns: The generated payload.
58 return "".join(choice(ascii_letters) for _ in range(length)).encode(
63 class PolicyAction(Enum):
66 BYPASS = ("bypass", 0)
67 DISCARD = ("discard", 1)
68 PROTECT = ("protect", 3)
70 def __init__(self, policy_name: str, policy_int_repr: int):
71 self.policy_name = policy_name
72 self.policy_int_repr = policy_int_repr
74 def __str__(self) -> str:
75 return self.policy_name
77 def __int__(self) -> int:
78 return self.policy_int_repr
81 class CryptoAlg(Enum):
82 """Encryption algorithms."""
84 AES_CBC_128 = ("aes-cbc-128", 1, "AES-CBC", 16)
85 AES_CBC_256 = ("aes-cbc-256", 3, "AES-CBC", 32)
86 AES_GCM_128 = ("aes-gcm-128", 7, "AES-GCM", 16)
87 AES_GCM_256 = ("aes-gcm-256", 9, "AES-GCM", 32)
90 self, alg_name: str, alg_int_repr: int, scapy_name: str, key_len: int
92 self.alg_name = alg_name
93 self.alg_int_repr = alg_int_repr
94 self.scapy_name = scapy_name
95 self.key_len = key_len
99 """Integrity algorithm."""
101 SHA_256_128 = ("sha-256-128", 4, "SHA2-256-128", 32)
102 SHA_512_256 = ("sha-512-256", 6, "SHA2-512-256", 64)
105 self, alg_name: str, alg_int_repr: int, scapy_name: str, key_len: int
107 self.alg_name = alg_name
108 self.alg_int_repr = alg_int_repr
109 self.scapy_name = scapy_name
110 self.key_len = key_len
113 class IPsecProto(IntEnum):
114 """IPsec protocol."""
116 IPSEC_API_PROTO_ESP = 50
117 IPSEC_API_PROTO_AH = 51
120 class IPsecSadFlags(IntEnum):
121 """IPsec Security Association Database flags."""
123 IPSEC_API_SAD_FLAG_NONE = 0
124 # Enable extended sequence numbers
125 IPSEC_API_SAD_FLAG_USE_ESN = 0x01
126 # Enable Anti - replay
127 IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02
128 # IPsec tunnel mode if non-zero, else transport mode
129 IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04
130 # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
131 # only valid if is_tunnel is non-zero
132 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08
133 # Enable UDP encapsulation for NAT traversal
134 IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10
135 # IPsec SA is or inbound traffic
136 IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
139 class TunnelEncpaDecapFlags(IntEnum):
140 """Flags controlling tunnel behaviour."""
142 TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
143 # at encap, copy the DF bit of the payload into the tunnel header
144 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
145 # at encap, set the DF bit in the tunnel header
146 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
147 # at encap, copy the DSCP bits of the payload into the tunnel header
148 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
149 # at encap, copy the ECN bit of the payload into the tunnel header
150 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
151 # at decap, copy the ECN bit of the tunnel header into the payload
152 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
155 class TunnelMode(IntEnum):
159 TUNNEL_API_MODE_P2P = 0
161 TUNNEL_API_MODE_MP = 1
165 """IPsec utilities."""
168 def policy_action_bypass() -> PolicyAction:
169 """Return policy action bypass.
171 :returns: PolicyAction enum BYPASS object.
174 return PolicyAction.BYPASS
177 def policy_action_discard() -> PolicyAction:
178 """Return policy action discard.
180 :returns: PolicyAction enum DISCARD object.
183 return PolicyAction.DISCARD
186 def policy_action_protect() -> PolicyAction:
187 """Return policy action protect.
189 :returns: PolicyAction enum PROTECT object.
192 return PolicyAction.PROTECT
195 def crypto_alg_aes_cbc_128() -> CryptoAlg:
196 """Return encryption algorithm aes-cbc-128.
198 :returns: CryptoAlg enum AES_CBC_128 object.
201 return CryptoAlg.AES_CBC_128
204 def crypto_alg_aes_cbc_256() -> CryptoAlg:
205 """Return encryption algorithm aes-cbc-256.
207 :returns: CryptoAlg enum AES_CBC_256 object.
210 return CryptoAlg.AES_CBC_256
213 def crypto_alg_aes_gcm_128() -> CryptoAlg:
214 """Return encryption algorithm aes-gcm-128.
216 :returns: CryptoAlg enum AES_GCM_128 object.
219 return CryptoAlg.AES_GCM_128
222 def crypto_alg_aes_gcm_256() -> CryptoAlg:
223 """Return encryption algorithm aes-gcm-256.
225 :returns: CryptoAlg enum AES_GCM_128 object.
228 return CryptoAlg.AES_GCM_256
231 def get_crypto_alg_key_len(crypto_alg: CryptoAlg) -> int:
232 """Return encryption algorithm key length.
234 :param crypto_alg: Encryption algorithm.
235 :type crypto_alg: CryptoAlg
236 :returns: Key length.
239 return crypto_alg.key_len
242 def get_crypto_alg_scapy_name(crypto_alg: CryptoAlg) -> str:
243 """Return encryption algorithm scapy name.
245 :param crypto_alg: Encryption algorithm.
246 :type crypto_alg: CryptoAlg
247 :returns: Algorithm scapy name.
250 return crypto_alg.scapy_name
253 def integ_alg_sha_256_128() -> IntegAlg:
254 """Return integrity algorithm SHA-256-128.
256 :returns: IntegAlg enum SHA_256_128 object.
259 return IntegAlg.SHA_256_128
262 def integ_alg_sha_512_256() -> IntegAlg:
263 """Return integrity algorithm SHA-512-256.
265 :returns: IntegAlg enum SHA_512_256 object.
268 return IntegAlg.SHA_512_256
271 def get_integ_alg_key_len(integ_alg: Optional[IntegAlg]) -> int:
272 """Return integrity algorithm key length.
274 None argument is accepted, returning zero.
276 :param integ_alg: Integrity algorithm.
277 :type integ_alg: Optional[IntegAlg]
278 :returns: Key length.
281 return 0 if integ_alg is None else integ_alg.key_len
284 def get_integ_alg_scapy_name(integ_alg: Optional[IntegAlg]) -> str:
285 """Return integrity algorithm scapy name.
287 :param integ_alg: Integrity algorithm.
288 :type integ_alg: IntegAlg
289 :returns: Algorithm scapy name.
292 return integ_alg.scapy_name
295 def ipsec_proto_esp() -> int:
296 """Return IPSec protocol ESP.
298 :returns: IPsecProto enum ESP object.
301 return int(IPsecProto.IPSEC_API_PROTO_ESP)
304 def ipsec_proto_ah() -> int:
305 """Return IPSec protocol AH.
307 :returns: IPsecProto enum AH object.
310 return int(IPsecProto.IPSEC_API_PROTO_AH)
313 def vpp_ipsec_select_backend(
314 node: dict, protocol: int, index: int = 1
316 """Select IPsec backend.
318 :param node: VPP node to select IPsec backend on.
319 :param protocol: IPsec protocol.
320 :param index: Backend index.
322 :type protocol: IPsecProto
324 :raises RuntimeError: If failed to select IPsec backend or if no API
327 cmd = "ipsec_select_backend"
328 err_msg = f"Failed to select IPsec backend on host {node['host']}"
329 args = dict(protocol=protocol, index=index)
330 with PapiSocketExecutor(node) as papi_exec:
331 papi_exec.add(cmd, **args).get_reply(err_msg)
334 def vpp_ipsec_set_async_mode(node: dict, async_enable: int = 1) -> None:
335 """Set IPsec async mode on|off.
337 Unconditionally, attempt to switch crypto dispatch into polling mode.
339 :param node: VPP node to set IPsec async mode.
340 :param async_enable: Async mode on or off.
342 :type async_enable: int
343 :raises RuntimeError: If failed to set IPsec async mode or if no API
346 with PapiSocketExecutor(node) as papi_exec:
347 cmd = "ipsec_set_async_mode"
348 err_msg = f"Failed to set IPsec async mode on host {node['host']}"
349 args = dict(async_enable=async_enable)
350 papi_exec.add(cmd, **args).get_reply(err_msg)
351 cmd = "crypto_set_async_dispatch_v2"
352 err_msg = "Failed to set dispatch mode."
353 args = dict(mode=0, adaptive=False)
355 papi_exec.add(cmd, **args).get_reply(err_msg)
356 except (AttributeError, RuntimeError):
357 # Expected when VPP build does not have the _v2 yet
358 # (after and before the first CRC check).
359 # TODO: Fail here when testing of pre-23.10 builds is over.
363 def vpp_ipsec_crypto_sw_scheduler_set_worker(
364 node: dict, workers: Iterable[int], crypto_enable: bool = False
366 """Enable or disable crypto on specific vpp worker threads.
368 :param node: VPP node to enable or disable crypto for worker threads.
369 :param workers: List of VPP thread numbers.
370 :param crypto_enable: Disable or enable crypto work.
372 :type workers: Iterable[int]
373 :type crypto_enable: bool
374 :raises RuntimeError: If failed to enable or disable crypto for worker
375 thread or if no API reply received.
377 for worker in workers:
378 cmd = "crypto_sw_scheduler_set_worker"
380 "Failed to disable/enable crypto for worker thread"
381 f" on host {node['host']}"
383 args = dict(worker_index=worker - 1, crypto_enable=crypto_enable)
384 with PapiSocketExecutor(node) as papi_exec:
385 papi_exec.add(cmd, **args).get_reply(err_msg)
388 def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
389 nodes: dict, crypto_enable: bool = False
391 """Enable or disable crypto on specific vpp worker threads.
393 :param node: VPP node to enable or disable crypto for worker threads.
394 :param crypto_enable: Disable or enable crypto work.
396 :type crypto_enable: bool
397 :raises RuntimeError: If failed to enable or disable crypto for worker
398 thread or if no API reply received.
400 for node_name, node in nodes.items():
401 if node["type"] == NodeType.DUT:
402 thread_data = VPPUtil.vpp_show_threads(node)
403 worker_cnt = len(thread_data) - 1
407 workers = BuiltIn().get_variable_value(
408 f"${{{node_name}_cpu_dp}}"
410 for item in thread_data:
411 if str(item.cpu_id) in workers.split(","):
412 worker_ids.append(item.id)
414 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
415 node, workers=worker_ids, crypto_enable=crypto_enable
419 def vpp_ipsec_add_sad_entry(
423 crypto_alg: CryptoAlg,
425 integ_alg: Optional[IntegAlg] = None,
427 tunnel_src: Optional[str] = None,
428 tunnel_dst: Optional[str] = None,
430 """Create Security Association Database entry on the VPP node.
432 :param node: VPP node to add SAD entry on.
433 :param sad_id: SAD entry ID.
434 :param spi: Security Parameter Index of this SAD entry.
435 :param crypto_alg: The encryption algorithm name.
436 :param crypto_key: The encryption key string.
437 :param integ_alg: The integrity algorithm name.
438 :param integ_key: The integrity key string.
439 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
440 specified ESP transport mode is used.
441 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
442 not specified ESP transport mode is used.
446 :type crypto_alg: CryptoAlg
447 :type crypto_key: str
448 :type integ_alg: Optional[IntegAlg]
450 :type tunnel_src: Optional[str]
451 :type tunnel_dst: Optional[str]
453 if isinstance(crypto_key, str):
454 crypto_key = crypto_key.encode(encoding="utf-8")
455 if isinstance(integ_key, str):
456 integ_key = integ_key.encode(encoding="utf-8")
457 ckey = dict(length=len(crypto_key), data=crypto_key)
458 ikey = dict(length=len(integ_key), data=integ_key if integ_key else 0)
460 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
461 if tunnel_src and tunnel_dst:
462 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
463 src_addr = ip_address(tunnel_src)
464 dst_addr = ip_address(tunnel_dst)
465 if src_addr.version == 6:
467 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
473 cmd = "ipsec_sad_entry_add_v2"
475 "Failed to add Security Association Database entry"
476 f" on host {node['host']}"
481 crypto_algorithm=crypto_alg.alg_int_repr,
483 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
490 encap_decap_flags=int(
491 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
493 dscp=int(IpDscp.IP_API_DSCP_CS0),
495 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
496 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
497 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
498 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
500 args = dict(entry=sad_entry)
501 with PapiSocketExecutor(node) as papi_exec:
502 papi_exec.add(cmd, **args).get_reply(err_msg)
505 def vpp_ipsec_add_sad_entries(
510 crypto_alg: CryptoAlg,
512 integ_alg: Optional[IntegAlg] = None,
514 tunnel_src: Optional[str] = None,
515 tunnel_dst: Optional[str] = None,
516 tunnel_addr_incr: bool = True,
518 """Create multiple Security Association Database entries on VPP node.
520 :param node: VPP node to add SAD entry on.
521 :param n_entries: Number of SAD entries to be created.
522 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
524 :param spi: Security Parameter Index of first SAD entry. All subsequent
525 SAD entries will have spi incremented by 1.
526 :param crypto_alg: The encryption algorithm name.
527 :param crypto_key: The encryption key string.
528 :param integ_alg: The integrity algorithm name.
529 :param integ_key: The integrity key string.
530 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
531 specified ESP transport mode is used.
532 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
533 not specified ESP transport mode is used.
534 :param tunnel_addr_incr: Enable or disable tunnel IP address
540 :type crypto_alg: CryptoAlg
541 :type crypto_key: str
542 :type integ_alg: Optional[IntegAlg]
544 :type tunnel_src: Optional[str]
545 :type tunnel_dst: Optional[str]
546 :type tunnel_addr_incr: bool
548 if isinstance(crypto_key, str):
549 crypto_key = crypto_key.encode(encoding="utf-8")
550 if isinstance(integ_key, str):
551 integ_key = integ_key.encode(encoding="utf-8")
552 if tunnel_src and tunnel_dst:
553 src_addr = ip_address(tunnel_src)
554 dst_addr = ip_address(tunnel_dst)
561 1 << (128 - 96) if src_addr.version == 6 else 1 << (32 - 24)
566 ckey = dict(length=len(crypto_key), data=crypto_key)
567 ikey = dict(length=len(integ_key), data=integ_key if integ_key else 0)
569 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
570 if tunnel_src and tunnel_dst:
571 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
572 if src_addr.version == 6:
574 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
577 cmd = "ipsec_sad_entry_add_v2"
579 "Failed to add Security Association Database entry"
580 f" on host {node['host']}"
586 crypto_algorithm=crypto_alg.alg_int_repr,
588 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
595 encap_decap_flags=int(
596 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
598 dscp=int(IpDscp.IP_API_DSCP_CS0),
600 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
601 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
602 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
603 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
605 args = dict(entry=sad_entry)
606 with PapiSocketExecutor(node, is_async=True) as papi_exec:
607 for i in range(n_entries):
608 args["entry"]["sad_id"] = int(sad_id) + i
609 args["entry"]["spi"] = int(spi) + i
610 args["entry"]["tunnel"]["src"] = (
611 str(src_addr + i * addr_incr)
612 if tunnel_src and tunnel_dst
615 args["entry"]["tunnel"]["dst"] = (
616 str(dst_addr + i * addr_incr)
617 if tunnel_src and tunnel_dst
620 history = bool(not 1 < i < n_entries - 2)
621 papi_exec.add(cmd, history=history, **args)
622 papi_exec.get_replies(err_msg)
625 def vpp_ipsec_set_ip_route(
633 dst_mac: Optional[str] = None,
635 """Set IP address and route on interface.
637 :param node: VPP node to add config on.
638 :param n_tunnels: Number of tunnels to create.
639 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
640 :param traffic_addr: Traffic destination IP address to route.
641 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
642 :param interface: Interface key on node 1.
643 :param raddr_range: Mask specifying range of Policy selector Remote IP
644 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
646 :param dst_mac: The MAC address of destination tunnels.
649 :type tunnel_src: str
650 :type traffic_addr: str
651 :type tunnel_dst: str
653 :type raddr_range: int
654 :type dst_mac: Optional[str]
656 tunnel_src = ip_address(tunnel_src)
657 tunnel_dst = ip_address(tunnel_dst)
658 traffic_addr = ip_address(traffic_addr)
659 tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
661 1 << (128 - raddr_range)
662 if tunnel_src.version == 6
663 else 1 << (32 - raddr_range)
666 cmd1 = "sw_interface_add_del_address"
668 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
673 cmd2 = "ip_route_add_del"
674 args2 = dict(is_add=1, is_multipath=0, route=None)
675 cmd3 = "ip_neighbor_add_del"
679 sw_if_index=Topology.get_interface_sw_index(node, interface),
681 mac_address=str(dst_mac),
686 "Failed to configure IP addresses, IP routes and"
687 f" IP neighbor on interface {interface} on host {node['host']}"
689 else "Failed to configure IP addresses and IP routes"
690 f" on interface {interface} on host {node['host']}"
693 with PapiSocketExecutor(node, is_async=True) as papi_exec:
694 for i in range(n_tunnels):
695 tunnel_dst_addr = tunnel_dst + i * addr_incr
696 args1["prefix"] = IPUtil.create_prefix_object(
697 tunnel_src + i * addr_incr, raddr_range
699 args2["route"] = IPUtil.compose_vpp_route_structure(
702 prefix_len=tunnel_dst_prefix,
704 gateway=tunnel_dst_addr,
706 history = bool(not 1 < i < n_tunnels - 2)
707 papi_exec.add(cmd1, history=history, **args1)
708 papi_exec.add(cmd2, history=history, **args2)
710 args2["route"] = IPUtil.compose_vpp_route_structure(
713 prefix_len=tunnel_dst_prefix,
715 gateway=tunnel_dst_addr,
717 papi_exec.add(cmd2, history=history, **args2)
720 args3["neighbor"]["ip_address"] = ip_address(
723 papi_exec.add(cmd3, history=history, **args3)
724 papi_exec.get_replies(err_msg)
727 def vpp_ipsec_add_spd(node: dict, spd_id: int) -> None:
728 """Create Security Policy Database on the VPP node.
730 :param node: VPP node to add SPD on.
731 :param spd_id: SPD ID.
735 cmd = "ipsec_spd_add_del"
737 f"Failed to add Security Policy Database on host {node['host']}"
739 args = dict(is_add=True, spd_id=int(spd_id))
740 with PapiSocketExecutor(node) as papi_exec:
741 papi_exec.add(cmd, **args).get_reply(err_msg)
744 def vpp_ipsec_spd_add_if(
745 node: dict, spd_id: int, interface: Union[str, int]
747 """Add interface to the Security Policy Database.
749 :param node: VPP node.
750 :param spd_id: SPD ID to add interface on.
751 :param interface: Interface name or sw_if_index.
754 :type interface: str or int
756 cmd = "ipsec_interface_add_del_spd"
758 f"Failed to add interface {interface} to Security Policy"
759 f" Database {spd_id} on host {node['host']}"
763 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
766 with PapiSocketExecutor(node) as papi_exec:
767 papi_exec.add(cmd, **args).get_reply(err_msg)
770 def vpp_ipsec_create_spds_match_nth_entry(
772 dir1_interface: Union[str, int],
773 dir2_interface: Union[str, int],
775 local_addr_range: Union[str, IPv4Address, IPv6Address],
776 remote_addr_range: Union[str, IPv4Address, IPv6Address],
777 action: PolicyAction = PolicyAction.BYPASS,
778 inbound: bool = False,
779 bidirectional: bool = True,
781 """Create one matching SPD entry for inbound or outbound traffic on
782 a DUT for each traffic direction and also create entry_amount - 1
783 non-matching SPD entries. Create a Security Policy Database on each
784 outbound interface where these entries will be configured.
785 The matching SPD entry will have the lowest priority, input action and
786 will be configured to match the IP flow. The non-matching entries will
787 be the same, except with higher priority and non-matching IP flows.
789 Action Protect is currently not supported.
791 :param node: VPP node to configured the SPDs and their entries.
792 :param dir1_interface: The interface in direction 1 where the entries
794 :param dir2_interface: The interface in direction 2 where the entries
796 :param entry_amount: The number of SPD entries to configure. If
797 entry_amount == 1, no non-matching entries will be configured.
798 :param local_addr_range: Matching local address range in direction 1
799 in format IP/prefix or IP/mask. If no mask is provided, it's
800 considered to be /32.
801 :param remote_addr_range: Matching remote address range in
802 direction 1 in format IP/prefix or IP/mask. If no mask is
803 provided, it's considered to be /32.
804 :param action: Policy action.
805 :param inbound: If True policy is for inbound traffic, otherwise
807 :param bidirectional: When True, will create SPDs in both directions
808 of traffic. When False, only in one direction.
810 :type dir1_interface: Union[str, int]
811 :type dir2_interface: Union[str, int]
812 :type entry_amount: int
813 :type local_addr_range:
814 Union[str, IPv4Address, IPv6Address]
815 :type remote_addr_range:
816 Union[str, IPv4Address, IPv6Address]
817 :type action: PolicyAction
819 :type bidirectional: bool
820 :raises NotImplementedError: When the action is PolicyAction.PROTECT.
823 if action == PolicyAction.PROTECT:
824 raise NotImplementedError("Policy action PROTECT is not supported.")
828 matching_priority = 1
830 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
831 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
832 # matching entry direction 1
833 IPsecUtil.vpp_ipsec_add_spd_entry(
839 laddr_range=local_addr_range,
840 raddr_range=remote_addr_range,
844 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
845 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
847 # matching entry direction 2, the address ranges are switched
848 IPsecUtil.vpp_ipsec_add_spd_entry(
854 laddr_range=remote_addr_range,
855 raddr_range=local_addr_range,
858 # non-matching entries
859 no_match_entry_amount = entry_amount - 1
860 if no_match_entry_amount > 0:
861 # create a NetworkIncrement representation of the network,
862 # then skip the matching network
863 no_match_local_addr_range = NetworkIncrement(
864 ip_network(local_addr_range)
866 next(no_match_local_addr_range)
868 no_match_remote_addr_range = NetworkIncrement(
869 ip_network(remote_addr_range)
871 next(no_match_remote_addr_range)
873 # non-matching entries direction 1
874 IPsecUtil.vpp_ipsec_add_spd_entries(
876 no_match_entry_amount,
878 ObjIncrement(matching_priority + 1, 1),
881 laddr_range=no_match_local_addr_range,
882 raddr_range=no_match_remote_addr_range,
886 # reset the networks so that we're using a unified config
887 # the address ranges are switched
888 no_match_remote_addr_range = NetworkIncrement(
889 ip_network(local_addr_range)
891 next(no_match_remote_addr_range)
893 no_match_local_addr_range = NetworkIncrement(
894 ip_network(remote_addr_range)
896 next(no_match_local_addr_range)
897 # non-matching entries direction 2
898 IPsecUtil.vpp_ipsec_add_spd_entries(
900 no_match_entry_amount,
902 ObjIncrement(matching_priority + 1, 1),
905 laddr_range=no_match_local_addr_range,
906 raddr_range=no_match_remote_addr_range,
909 IPsecUtil.vpp_ipsec_show_all(node)
912 def _vpp_ipsec_add_spd_entry_internal(
913 executor: PapiSocketExecutor,
916 action: PolicyAction,
917 inbound: bool = True,
918 sa_id: Optional[int] = None,
919 proto: Optional[int] = None,
920 laddr_range: Optional[str] = None,
921 raddr_range: Optional[str] = None,
922 lport_range: Optional[str] = None,
923 rport_range: Optional[str] = None,
924 is_ipv6: bool = False,
926 """Prepare to create Security Policy Database entry on the VPP node.
928 This just adds one more command to the executor.
929 The call site shall get replies once all entries are added,
930 to get speed benefit from async PAPI.
932 :param executor: Open PAPI executor (async handling) to add commands to.
933 :param spd_id: SPD ID to add entry on.
934 :param priority: SPD entry priority, higher number = higher priority.
935 :param action: Policy action.
936 :param inbound: If True policy is for inbound traffic, otherwise
938 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
939 :param proto: Policy selector next layer protocol number.
940 :param laddr_range: Policy selector local IPv4 or IPv6 address range
941 in format IP/prefix or IP/mask. If no mask is provided,
942 it's considered to be /32.
943 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
944 in format IP/prefix or IP/mask. If no mask is provided,
945 it's considered to be /32.
946 :param lport_range: Policy selector local TCP/UDP port range in format
947 <port_start>-<port_end>.
948 :param rport_range: Policy selector remote TCP/UDP port range in format
949 <port_start>-<port_end>.
950 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
951 not defined so it will default to address ::/0, otherwise False.
952 :type executor: PapiSocketExecutor
955 :type action: PolicyAction
957 :type sa_id: Optional[int]
958 :type proto: Optional[int]
959 :type laddr_range: Optional[str]
960 :type raddr_range: Optional[str]
961 :type lport_range: Optional[str]
962 :type rport_range: Optional[str]
965 if laddr_range is None:
966 laddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
968 if raddr_range is None:
969 raddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
971 local_net = ip_network(laddr_range, strict=False)
972 remote_net = ip_network(raddr_range, strict=False)
974 cmd = "ipsec_spd_entry_add_del_v2"
978 priority=int(priority),
979 is_outbound=not inbound,
980 sa_id=int(sa_id) if sa_id else 0,
982 protocol=255 if proto is None else int(proto),
983 remote_address_start=IPAddress.create_ip_address_object(
984 remote_net.network_address
986 remote_address_stop=IPAddress.create_ip_address_object(
987 remote_net.broadcast_address
989 local_address_start=IPAddress.create_ip_address_object(
990 local_net.network_address
992 local_address_stop=IPAddress.create_ip_address_object(
993 local_net.broadcast_address
996 int(rport_range.split("-")[0]) if rport_range else 0
999 int(rport_range.split("-")[1]) if rport_range else 65535
1002 int(lport_range.split("-")[0]) if lport_range else 0
1005 int(lport_range.split("-")[1]) if rport_range else 65535
1008 args = dict(is_add=True, entry=spd_entry)
1009 executor.add(cmd, **args)
1012 def vpp_ipsec_add_spd_entry(
1016 action: PolicyAction,
1017 inbound: bool = True,
1018 sa_id: Optional[int] = None,
1019 proto: Optional[int] = None,
1020 laddr_range: Optional[str] = None,
1021 raddr_range: Optional[str] = None,
1022 lport_range: Optional[str] = None,
1023 rport_range: Optional[str] = None,
1024 is_ipv6: bool = False,
1026 """Create Security Policy Database entry on the VPP node.
1028 :param node: VPP node to add SPD entry on.
1029 :param spd_id: SPD ID to add entry on.
1030 :param priority: SPD entry priority, higher number = higher priority.
1031 :param action: Policy action.
1032 :param inbound: If True policy is for inbound traffic, otherwise
1034 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
1035 :param proto: Policy selector next layer protocol number.
1036 :param laddr_range: Policy selector local IPv4 or IPv6 address range
1037 in format IP/prefix or IP/mask. If no mask is provided,
1038 it's considered to be /32.
1039 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
1040 in format IP/prefix or IP/mask. If no mask is provided,
1041 it's considered to be /32.
1042 :param lport_range: Policy selector local TCP/UDP port range in format
1043 <port_start>-<port_end>.
1044 :param rport_range: Policy selector remote TCP/UDP port range in format
1045 <port_start>-<port_end>.
1046 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
1047 not defined so it will default to address ::/0, otherwise False.
1051 :type action: PolicyAction
1053 :type sa_id: Optional[int]
1054 :type proto: Optional[int]
1055 :type laddr_range: Optional[str]
1056 :type raddr_range: Optional[str]
1057 :type lport_range: Optional[str]
1058 :type rport_range: Optional[str]
1062 "Failed to add entry to Security Policy Database"
1063 f" {spd_id} on host {node['host']}"
1065 with PapiSocketExecutor(node, is_async=True) as papi_exec:
1066 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
1080 papi_exec.get_replies(err_msg)
1083 def vpp_ipsec_add_spd_entries(
1087 priority: Optional[ObjIncrement],
1088 action: PolicyAction,
1090 sa_id: Optional[ObjIncrement] = None,
1091 proto: Optional[int] = None,
1092 laddr_range: Optional[NetworkIncrement] = None,
1093 raddr_range: Optional[NetworkIncrement] = None,
1094 lport_range: Optional[str] = None,
1095 rport_range: Optional[str] = None,
1096 is_ipv6: bool = False,
1098 """Create multiple Security Policy Database entries on the VPP node.
1100 :param node: VPP node to add SPD entries on.
1101 :param n_entries: Number of SPD entries to be added.
1102 :param spd_id: SPD ID to add entries on.
1103 :param priority: SPD entries priority, higher number = higher priority.
1104 :param action: Policy action.
1105 :param inbound: If True policy is for inbound traffic, otherwise
1107 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
1108 :param proto: Policy selector next layer protocol number.
1109 :param laddr_range: Policy selector local IPv4 or IPv6 address range
1110 in format IP/prefix or IP/mask. If no mask is provided,
1111 it's considered to be /32.
1112 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
1113 in format IP/prefix or IP/mask. If no mask is provided,
1114 it's considered to be /32.
1115 :param lport_range: Policy selector local TCP/UDP port range in format
1116 <port_start>-<port_end>.
1117 :param rport_range: Policy selector remote TCP/UDP port range in format
1118 <port_start>-<port_end>.
1119 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
1120 not defined so it will default to address ::/0, otherwise False.
1122 :type n_entries: int
1124 :type priority: Optional[ObjIncrement]
1125 :type action: PolicyAction
1127 :type sa_id: Optional[ObjIncrement]
1128 :type proto: Optional[int]
1129 :type laddr_range: Optional[NetworkIncrement]
1130 :type raddr_range: Optional[NetworkIncrement]
1131 :type lport_range: Optional[str]
1132 :type rport_range: Optional[str]
1135 if laddr_range is None:
1136 laddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
1137 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1139 if raddr_range is None:
1140 raddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
1141 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1144 "Failed to add entry to Security Policy Database"
1145 f" {spd_id} on host {node['host']}"
1147 with PapiSocketExecutor(node, is_async=True) as papi_exec:
1148 for _ in range(n_entries):
1149 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
1155 next(sa_id) if sa_id is not None else sa_id,
1163 papi_exec.get_replies(err_msg)
1166 def _ipsec_create_loopback_dut1_papi(
1167 nodes: dict, tun_ips: dict, if1_key: str, if2_key: str
1169 """Create loopback interface and set IP address on VPP node 1 interface
1172 :param nodes: VPP nodes to create tunnel interfaces.
1173 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1174 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1175 IPv4/IPv6 address (ip2).
1176 :param if1_key: VPP node 1 interface key from topology file.
1177 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1178 interface key from topology file.
1183 :returns: sw_if_idx Of the created loopback interface.
1186 with PapiSocketExecutor(nodes["DUT1"]) as papi_exec:
1187 # Create loopback interface on DUT1, set it to up state
1188 cmd = "create_loopback_instance"
1195 "Failed to create loopback interface"
1196 f" on host {nodes['DUT1']['host']}"
1198 papi_exec.add(cmd, **args)
1199 loop_sw_if_idx = papi_exec.get_sw_if_index(err_msg)
1200 cmd = "sw_interface_set_flags"
1202 sw_if_index=loop_sw_if_idx,
1203 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value,
1206 "Failed to set loopback interface state up"
1207 f" on host {nodes['DUT1']['host']}"
1209 papi_exec.add(cmd, **args).get_reply(err_msg)
1210 # Set IP address on VPP node 1 interface
1211 cmd = "sw_interface_add_del_address"
1213 sw_if_index=InterfaceUtil.get_interface_index(
1214 nodes["DUT1"], if1_key
1218 prefix=IPUtil.create_prefix_object(
1220 96 if tun_ips["ip2"].version == 6 else 24,
1224 f"Failed to set IP address on interface {if1_key}"
1225 f" on host {nodes['DUT1']['host']}"
1227 papi_exec.add(cmd, **args).get_reply(err_msg)
1228 cmd2 = "ip_neighbor_add_del"
1232 sw_if_index=Topology.get_interface_sw_index(
1233 nodes["DUT1"], if1_key
1237 Topology.get_interface_mac(nodes["DUT2"], if2_key)
1238 if "DUT2" in nodes.keys()
1239 else Topology.get_interface_mac(nodes["TG"], if2_key)
1241 ip_address=tun_ips["ip2"].compressed,
1244 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1245 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1247 return loop_sw_if_idx
1250 def _ipsec_create_tunnel_interfaces_dut1_papi(
1256 crypto_alg: CryptoAlg,
1257 integ_alg: Optional[IntegAlg],
1258 raddr_ip2: Union[IPv4Address, IPv6Address],
1261 existing_tunnels: int = 0,
1262 ) -> Tuple[List[bytes], List[bytes]]:
1263 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1265 Generate random keys and return them (so DUT2 or TG can decrypt).
1267 :param nodes: VPP nodes to create tunnel interfaces.
1268 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1269 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1270 IPv4/IPv6 address (ip2).
1271 :param if1_key: VPP node 1 interface key from topology file.
1272 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1273 interface key from topology file.
1274 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1275 :param crypto_alg: The encryption algorithm name.
1276 :param integ_alg: The integrity algorithm name.
1277 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1278 first tunnel in direction node2->node1.
1279 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1280 :param addr_incr: IP / IPv6 address incremental step.
1281 :param existing_tunnels: Number of tunnel interfaces before creation.
1282 Useful mainly for reconf tests. Default 0.
1287 :type n_tunnels: int
1288 :type crypto_alg: CryptoAlg
1289 :type integ_alg: Optional[IntegAlg]
1290 :type raddr_ip2: Union[IPv4Address, IPv6Address]
1291 :type addr_incr: int
1293 :type existing_tunnels: int
1294 :returns: Generated ckeys and ikeys.
1295 :rtype: List[bytes], List[bytes]
1297 if not existing_tunnels:
1298 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1299 nodes, tun_ips, if1_key, if2_key
1302 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1303 nodes["DUT1"], "loop0"
1305 with PapiSocketExecutor(nodes["DUT1"], is_async=True) as papi_exec:
1306 # Configure IP addresses on loop0 interface
1307 cmd = "sw_interface_add_del_address"
1309 sw_if_index=loop_sw_if_idx,
1314 for i in range(existing_tunnels, n_tunnels):
1315 args["prefix"] = IPUtil.create_prefix_object(
1316 tun_ips["ip1"] + i * addr_incr,
1317 128 if tun_ips["ip1"].version == 6 else 32,
1320 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1322 # Configure IPIP tunnel interfaces
1323 cmd = "ipip_add_tunnel"
1325 instance=Constants.BITWISE_NON_ZERO,
1330 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1332 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1333 dscp=int(IpDscp.IP_API_DSCP_CS0),
1335 args = dict(tunnel=ipip_tunnel)
1336 ipip_tunnels = [None] * existing_tunnels
1337 for i in range(existing_tunnels, n_tunnels):
1338 ipip_tunnel["src"] = IPAddress.create_ip_address_object(
1339 tun_ips["ip1"] + i * addr_incr
1341 ipip_tunnel["dst"] = IPAddress.create_ip_address_object(
1345 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1348 "Failed to add IPIP tunnel interfaces on host"
1349 f" {nodes['DUT1']['host']}"
1351 ipip_tunnels.extend(
1353 reply["sw_if_index"]
1354 for reply in papi_exec.get_replies(err_msg)
1355 if "sw_if_index" in reply
1358 # Configure IPSec SAD entries
1359 ckeys = [bytes()] * existing_tunnels
1360 ikeys = [bytes()] * existing_tunnels
1361 cmd = "ipsec_sad_entry_add_v2"
1362 c_key = dict(length=0, data=None)
1363 i_key = dict(length=0, data=None)
1364 common_flags = IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1368 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1369 crypto_algorithm=crypto_alg.alg_int_repr,
1371 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1372 integrity_key=i_key,
1378 encap_decap_flags=int(
1379 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1381 dscp=int(IpDscp.IP_API_DSCP_CS0),
1384 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
1385 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
1386 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
1388 args = dict(entry=sad_entry)
1389 for i in range(existing_tunnels, n_tunnels):
1391 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1394 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1396 # SAD entry for outband / tx path
1397 sad_entry["sad_id"] = i
1398 sad_entry["spi"] = spi_d["spi_1"] + i
1400 sad_entry["crypto_key"]["length"] = len(ckeys[i])
1401 sad_entry["crypto_key"]["data"] = ckeys[i]
1403 sad_entry["integrity_key"]["length"] = len(ikeys[i])
1404 sad_entry["integrity_key"]["data"] = ikeys[i]
1406 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1408 sad_entry["flags"] |= IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1409 for i in range(existing_tunnels, n_tunnels):
1410 # SAD entry for inband / rx path
1411 sad_entry["sad_id"] = 100000 + i
1412 sad_entry["spi"] = spi_d["spi_2"] + i
1414 sad_entry["crypto_key"]["length"] = len(ckeys[i])
1415 sad_entry["crypto_key"]["data"] = ckeys[i]
1417 sad_entry["integrity_key"]["length"] = len(ikeys[i])
1418 sad_entry["integrity_key"]["data"] = ikeys[i]
1420 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1423 "Failed to add IPsec SAD entries on host"
1424 f" {nodes['DUT1']['host']}"
1426 papi_exec.get_replies(err_msg)
1427 # Add protection for tunnels with IPSEC
1428 cmd = "ipsec_tunnel_protect_update"
1431 via_label=MPLS_LABEL_INVALID,
1432 obj_id=Constants.BITWISE_NON_ZERO,
1434 ipsec_tunnel_protect = dict(
1435 sw_if_index=None, nh=n_hop, sa_out=None, n_sa_in=1, sa_in=None
1437 args = dict(tunnel=ipsec_tunnel_protect)
1438 for i in range(existing_tunnels, n_tunnels):
1439 args["tunnel"]["sw_if_index"] = ipip_tunnels[i]
1440 args["tunnel"]["sa_out"] = i
1441 args["tunnel"]["sa_in"] = [100000 + i]
1443 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1446 "Failed to add protection for tunnels with IPSEC"
1447 f" on host {nodes['DUT1']['host']}"
1449 papi_exec.get_replies(err_msg)
1451 # Configure unnumbered interfaces
1452 cmd = "sw_interface_set_unnumbered"
1455 sw_if_index=InterfaceUtil.get_interface_index(
1456 nodes["DUT1"], if1_key
1458 unnumbered_sw_if_index=0,
1460 for i in range(existing_tunnels, n_tunnels):
1461 args["unnumbered_sw_if_index"] = ipip_tunnels[i]
1463 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1466 cmd = "sw_interface_set_flags"
1469 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value,
1471 for i in range(existing_tunnels, n_tunnels):
1472 args["sw_if_index"] = ipip_tunnels[i]
1474 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1476 # Configure IP routes
1477 cmd = "ip_route_add_del"
1478 args = dict(is_add=1, is_multipath=0, route=None)
1479 for i in range(existing_tunnels, n_tunnels):
1480 args["route"] = IPUtil.compose_vpp_route_structure(
1482 (raddr_ip2 + i).compressed,
1483 prefix_len=128 if raddr_ip2.version == 6 else 32,
1484 interface=ipip_tunnels[i],
1487 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1489 err_msg = f"Failed to add IP routes on host {nodes['DUT1']['host']}"
1490 papi_exec.get_replies(err_msg)
1495 def _ipsec_create_tunnel_interfaces_dut2_papi(
1500 crypto_alg: CryptoAlg,
1501 ckeys: Sequence[bytes],
1502 integ_alg: Optional[IntegAlg],
1503 ikeys: Sequence[bytes],
1504 raddr_ip1: Union[IPv4Address, IPv6Address],
1507 existing_tunnels: int = 0,
1509 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1511 This method accesses keys generated by DUT1 method
1512 and does not return anything.
1514 :param nodes: VPP nodes to create tunnel interfaces.
1515 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1516 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1517 IPv4/IPv6 address (ip2).
1518 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1519 interface key from topology file.
1520 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1521 :param crypto_alg: The encryption algorithm name.
1522 :param ckeys: List of encryption keys.
1523 :param integ_alg: The integrity algorithm name.
1524 :param ikeys: List of integrity keys.
1525 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1526 first tunnel in direction node1->node2.
1527 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1528 :param addr_incr: IP / IPv6 address incremental step.
1529 :param existing_tunnels: Number of tunnel interfaces before creation.
1530 Useful mainly for reconf tests. Default 0.
1534 :type n_tunnels: int
1535 :type crypto_alg: CryptoAlg
1536 :type ckeys: Sequence[bytes]
1537 :type integ_alg: Optional[IntegAlg]
1538 :type ikeys: Sequence[bytes]
1539 :type raddr_ip1: Union[IPv4Address, IPv6Address]
1540 :type addr_incr: int
1542 :type existing_tunnels: int
1544 with PapiSocketExecutor(nodes["DUT2"], is_async=True) as papi_exec:
1545 if not existing_tunnels:
1546 # Set IP address on VPP node 2 interface
1547 cmd = "sw_interface_add_del_address"
1549 sw_if_index=InterfaceUtil.get_interface_index(
1550 nodes["DUT2"], if2_key
1554 prefix=IPUtil.create_prefix_object(
1556 96 if tun_ips["ip2"].version == 6 else 24,
1560 f"Failed to set IP address on interface {if2_key}"
1561 f" on host {nodes['DUT2']['host']}"
1563 papi_exec.add(cmd, **args).get_replies(err_msg)
1564 # Configure IPIP tunnel interfaces
1565 cmd = "ipip_add_tunnel"
1567 instance=Constants.BITWISE_NON_ZERO,
1572 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1574 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1575 dscp=int(IpDscp.IP_API_DSCP_CS0),
1577 args = dict(tunnel=ipip_tunnel)
1578 ipip_tunnels = [None] * existing_tunnels
1579 for i in range(existing_tunnels, n_tunnels):
1580 ipip_tunnel["src"] = IPAddress.create_ip_address_object(
1583 ipip_tunnel["dst"] = IPAddress.create_ip_address_object(
1584 tun_ips["ip1"] + i * addr_incr
1587 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1590 "Failed to add IPIP tunnel interfaces on host"
1591 f" {nodes['DUT2']['host']}"
1593 ipip_tunnels.extend(
1595 reply["sw_if_index"]
1596 for reply in papi_exec.get_replies(err_msg)
1597 if "sw_if_index" in reply
1600 # Configure IPSec SAD entries
1601 cmd = "ipsec_sad_entry_add_v2"
1602 c_key = dict(length=0, data=None)
1603 i_key = dict(length=0, data=None)
1604 common_flags = IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1608 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1609 crypto_algorithm=crypto_alg.alg_int_repr,
1611 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1612 integrity_key=i_key,
1618 encap_decap_flags=int(
1619 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1621 dscp=int(IpDscp.IP_API_DSCP_CS0),
1624 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
1625 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
1626 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
1628 args = dict(entry=sad_entry)
1629 for i in range(existing_tunnels, n_tunnels):
1631 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1634 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1636 # SAD entry for outband / tx path
1637 sad_entry["sad_id"] = 100000 + i
1638 sad_entry["spi"] = spi_d["spi_2"] + i
1640 sad_entry["crypto_key"]["length"] = len(ckeys[i])
1641 sad_entry["crypto_key"]["data"] = ckeys[i]
1643 sad_entry["integrity_key"]["length"] = len(ikeys[i])
1644 sad_entry["integrity_key"]["data"] = ikeys[i]
1646 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1648 sad_entry["flags"] |= IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1649 for i in range(existing_tunnels, n_tunnels):
1650 # SAD entry for inband / rx path
1651 sad_entry["sad_id"] = i
1652 sad_entry["spi"] = spi_d["spi_1"] + i
1654 sad_entry["crypto_key"]["length"] = len(ckeys[i])
1655 sad_entry["crypto_key"]["data"] = ckeys[i]
1657 sad_entry["integrity_key"]["length"] = len(ikeys[i])
1658 sad_entry["integrity_key"]["data"] = ikeys[i]
1660 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1663 f"Failed to add IPsec SAD entries on host"
1664 f" {nodes['DUT2']['host']}"
1666 papi_exec.get_replies(err_msg)
1667 # Add protection for tunnels with IPSEC
1668 cmd = "ipsec_tunnel_protect_update"
1671 via_label=MPLS_LABEL_INVALID,
1672 obj_id=Constants.BITWISE_NON_ZERO,
1674 ipsec_tunnel_protect = dict(
1675 sw_if_index=None, nh=n_hop, sa_out=None, n_sa_in=1, sa_in=None
1677 args = dict(tunnel=ipsec_tunnel_protect)
1678 for i in range(existing_tunnels, n_tunnels):
1679 args["tunnel"]["sw_if_index"] = ipip_tunnels[i]
1680 args["tunnel"]["sa_out"] = 100000 + i
1681 args["tunnel"]["sa_in"] = [i]
1683 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1686 "Failed to add protection for tunnels with IPSEC"
1687 f" on host {nodes['DUT2']['host']}"
1689 papi_exec.get_replies(err_msg)
1691 if not existing_tunnels:
1692 # Configure IP route
1693 cmd = "ip_route_add_del"
1694 route = IPUtil.compose_vpp_route_structure(
1696 tun_ips["ip1"].compressed,
1697 prefix_len=32 if tun_ips["ip1"].version == 6 else 8,
1699 gateway=(tun_ips["ip2"] - 1).compressed,
1701 args = dict(is_add=1, is_multipath=0, route=route)
1702 papi_exec.add(cmd, **args)
1703 # Configure unnumbered interfaces
1704 cmd = "sw_interface_set_unnumbered"
1707 sw_if_index=InterfaceUtil.get_interface_index(
1708 nodes["DUT2"], if2_key
1710 unnumbered_sw_if_index=0,
1712 for i in range(existing_tunnels, n_tunnels):
1713 args["unnumbered_sw_if_index"] = ipip_tunnels[i]
1715 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1718 cmd = "sw_interface_set_flags"
1721 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value,
1723 for i in range(existing_tunnels, n_tunnels):
1724 args["sw_if_index"] = ipip_tunnels[i]
1726 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1728 # Configure IP routes
1729 cmd = "ip_route_add_del"
1730 args = dict(is_add=1, is_multipath=0, route=None)
1731 for i in range(existing_tunnels, n_tunnels):
1732 args["route"] = IPUtil.compose_vpp_route_structure(
1734 (raddr_ip1 + i).compressed,
1735 prefix_len=128 if raddr_ip1.version == 6 else 32,
1736 interface=ipip_tunnels[i],
1739 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1741 err_msg = f"Failed to add IP routes on host {nodes['DUT2']['host']}"
1742 papi_exec.get_replies(err_msg)
1745 def vpp_ipsec_create_tunnel_interfaces(
1747 tun_if1_ip_addr: str,
1748 tun_if2_ip_addr: str,
1752 crypto_alg: CryptoAlg,
1753 integ_alg: Optional[IntegAlg],
1757 existing_tunnels: int = 0,
1758 return_keys: bool = False,
1759 ) -> Optional[Tuple[List[bytes], List[bytes], int, int]]:
1760 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1762 Some deployments (e.g. devicetest) need to know the generated keys.
1763 But other deployments (e.g. scale perf test) would get spammed
1764 if we returned keys every time.
1766 :param nodes: VPP nodes to create tunnel interfaces.
1767 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1769 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1771 :param if1_key: VPP node 1 interface key from topology file.
1772 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1773 interface key from topology file.
1774 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1775 :param crypto_alg: The encryption algorithm name.
1776 :param integ_alg: The integrity algorithm name.
1777 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1778 first tunnel in direction node1->node2.
1779 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1780 first tunnel in direction node2->node1.
1781 :param raddr_range: Mask specifying range of Policy selector Remote
1782 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1783 and to 128 in case of IPv6.
1784 :param existing_tunnels: Number of tunnel interfaces before creation.
1785 Useful mainly for reconf tests. Default 0.
1786 :param return_keys: Whether generated keys should be returned.
1788 :type tun_if1_ip_addr: str
1789 :type tun_if2_ip_addr: str
1792 :type n_tunnels: int
1793 :type crypto_alg: CryptoAlg
1794 :type integ_alg: Optional[IntegAlg]
1795 :type raddr_ip1: str
1796 :type raddr_ip2: str
1797 :type raddr_range: int
1798 :type existing_tunnels: int
1799 :type return_keys: bool
1800 :returns: Ckeys, ikeys, spi_1, spi_2.
1801 :rtype: Optional[Tuple[List[bytes], List[bytes], int, int]]
1803 n_tunnels = int(n_tunnels)
1804 existing_tunnels = int(existing_tunnels)
1805 spi_d = dict(spi_1=100000, spi_2=200000)
1807 ip1=ip_address(tun_if1_ip_addr), ip2=ip_address(tun_if2_ip_addr)
1809 raddr_ip1 = ip_address(raddr_ip1)
1810 raddr_ip2 = ip_address(raddr_ip2)
1812 1 << (128 - raddr_range)
1813 if tun_ips["ip1"].version == 6
1814 else 1 << (32 - raddr_range)
1817 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1830 if "DUT2" in nodes.keys():
1831 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1847 return ckeys, ikeys, spi_d["spi_1"], spi_d["spi_2"]
1851 def _create_ipsec_script_files(
1852 dut: str, instances: int
1853 ) -> List[TextIOWrapper]:
1854 """Create script files for configuring IPsec in containers
1856 :param dut: DUT node on which to create the script files
1857 :param instances: number of containers on DUT node
1859 :type instances: int
1860 :returns: Created opened file handles.
1861 :rtype: List[TextIOWrapper]
1864 for cnf in range(0, instances):
1866 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1868 scripts.append(open(script_filename, "w", encoding="utf-8"))
1872 def _close_and_copy_ipsec_script_files(
1873 dut: str, nodes: dict, instances: int, scripts: Sequence[TextIOWrapper]
1875 """Close created scripts and copy them to containers
1877 :param dut: DUT node on which to create the script files
1878 :param nodes: VPP nodes
1879 :param instances: number of containers on DUT node
1880 :param scripts: dictionary holding the script files
1883 :type instances: int
1886 for cnf in range(0, instances):
1887 scripts[cnf].close()
1889 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1891 scp_node(nodes[dut], script_filename, script_filename)
1894 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1899 crypto_alg: CryptoAlg,
1900 integ_alg: Optional[IntegAlg],
1906 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1908 :param nodes: VPP nodes to create tunnel interfaces.
1909 :param if1_ip_addr: VPP node 1 interface IP4 address.
1910 :param if2_ip_addr: VPP node 2 interface IP4 address.
1911 :param n_tunnels: Number of tunnell interfaces to create.
1912 :param crypto_alg: The encryption algorithm name.
1913 :param integ_alg: The integrity algorithm name.
1914 :param raddr_ip1: Policy selector remote IPv4 start address for the
1915 first tunnel in direction node1->node2.
1916 :param raddr_ip2: Policy selector remote IPv4 start address for the
1917 first tunnel in direction node2->node1.
1918 :param raddr_range: Mask specifying range of Policy selector Remote
1919 IPv4 addresses. Valid values are from 1 to 32.
1920 :param n_instances: Number of containers.
1922 :type if1_ip_addr: str
1923 :type if2_ip_addr: str
1924 :type n_tunnels: int
1925 :type crypto_alg: CryptoAlg
1926 :type integ_alg: Optional[IntegAlg]
1927 :type raddr_ip1: str
1928 :type raddr_ip2: str
1929 :type raddr_range: int
1930 :type n_instances: int
1934 addr_incr = 1 << (32 - raddr_range)
1936 dut1_scripts = IPsecUtil._create_ipsec_script_files("DUT1", n_instances)
1937 dut2_scripts = IPsecUtil._create_ipsec_script_files("DUT2", n_instances)
1939 for cnf in range(0, n_instances):
1940 dut1_scripts[cnf].write(
1941 "create loopback interface\nset interface state loop0 up\n\n"
1943 dut2_scripts[cnf].write(
1944 f"ip route add {if1_ip_addr}/8 via"
1945 f" {ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1948 for tnl in range(0, n_tunnels):
1949 cnf = tnl % n_instances
1951 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), "hex"
1955 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), "hex"
1959 f"integ-alg {integ_alg.alg_name}"
1960 f" local-integ-key {ikey}"
1961 f" remote-integ-key {ikey}"
1963 # Configure tunnel end point(s) on left side
1964 dut1_scripts[cnf].write(
1965 "set interface ip address loop0"
1966 f" {ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1967 "create ipsec tunnel"
1968 f" local-ip {ip_address(if1_ip_addr) + tnl * addr_incr}"
1969 f" local-spi {spi_1 + tnl}"
1970 f" remote-ip {ip_address(if2_ip_addr) + cnf}"
1971 f" remote-spi {spi_2 + tnl}"
1972 f" crypto-alg {crypto_alg.alg_name}"
1973 f" local-crypto-key {ckey}"
1974 f" remote-crypto-key {ckey}"
1975 f" instance {tnl // n_instances}"
1976 f" salt 0x0 {integ}\n"
1977 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1978 f"set interface state ipip{tnl // n_instances} up\n"
1979 f"ip route add {ip_address(raddr_ip2)+tnl}/32"
1980 f" via ipip{tnl // n_instances}\n\n"
1982 # Configure tunnel end point(s) on right side
1983 dut2_scripts[cnf].write(
1984 f"set ip neighbor memif1/{cnf + 1}"
1985 f" {ip_address(if1_ip_addr) + tnl * addr_incr}"
1986 f" 02:02:00:00:{17:02X}:{cnf:02X} static\n"
1987 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf}"
1988 f" local-spi {spi_2 + tnl}"
1989 f" remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr}"
1990 f" remote-spi {spi_1 + tnl}"
1991 f" crypto-alg {crypto_alg.alg_name}"
1992 f" local-crypto-key {ckey}"
1993 f" remote-crypto-key {ckey}"
1994 f" instance {tnl // n_instances}"
1995 f" salt 0x0 {integ}\n"
1996 f"set interface unnumbered ipip{tnl // n_instances}"
1997 f" use memif1/{cnf + 1}\n"
1998 f"set interface state ipip{tnl // n_instances} up\n"
1999 f"ip route add {ip_address(raddr_ip1) + tnl}/32"
2000 f" via ipip{tnl // n_instances}\n\n"
2003 IPsecUtil._close_and_copy_ipsec_script_files(
2004 "DUT1", nodes, n_instances, dut1_scripts
2006 IPsecUtil._close_and_copy_ipsec_script_files(
2007 "DUT2", nodes, n_instances, dut2_scripts
2011 def vpp_ipsec_add_multiple_tunnels(
2013 interface1: Union[str, int],
2014 interface2: Union[str, int],
2016 crypto_alg: CryptoAlg,
2017 integ_alg: Optional[IntegAlg],
2023 tunnel_addr_incr: bool = True,
2025 """Create multiple IPsec tunnels between two VPP nodes.
2027 :param nodes: VPP nodes to create tunnels.
2028 :param interface1: Interface name or sw_if_index on node 1.
2029 :param interface2: Interface name or sw_if_index on node 2.
2030 :param n_tunnels: Number of tunnels to create.
2031 :param crypto_alg: The encryption algorithm name.
2032 :param integ_alg: The integrity algorithm name.
2033 :param tunnel_ip1: Tunnel node1 IPv4 address.
2034 :param tunnel_ip2: Tunnel node2 IPv4 address.
2035 :param raddr_ip1: Policy selector remote IPv4 start address for the
2036 first tunnel in direction node1->node2.
2037 :param raddr_ip2: Policy selector remote IPv4 start address for the
2038 first tunnel in direction node2->node1.
2039 :param raddr_range: Mask specifying range of Policy selector Remote
2040 IPv4 addresses. Valid values are from 1 to 32.
2041 :param tunnel_addr_incr: Enable or disable tunnel IP address
2044 :type interface1: Union[str, int]
2045 :type interface2: Union[str, int]
2046 :type n_tunnels: int
2047 :type crypto_alg: CryptoAlg
2048 :type integ_alg: Optional[IntegAlg]
2049 :type tunnel_ip1: str
2050 :type tunnel_ip2: str
2051 :type raddr_ip1: str
2052 :type raddr_ip2: str
2053 :type raddr_range: int
2054 :type tunnel_addr_incr: bool
2064 crypto_key = gen_key(
2065 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2068 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)).decode()
2074 Topology.get_interface_mac(nodes["DUT2"], interface2)
2075 if "DUT2" in nodes.keys()
2076 else Topology.get_interface_mac(nodes["TG"], interface2)
2078 IPsecUtil.vpp_ipsec_set_ip_route(
2089 IPsecUtil.vpp_ipsec_add_spd(nodes["DUT1"], spd_id)
2090 IPsecUtil.vpp_ipsec_spd_add_if(nodes["DUT1"], spd_id, interface1)
2094 if ip_address(tunnel_ip1).version == 6
2097 for i in range(n_tunnels // (addr_incr**2) + 1):
2098 dut1_local_outbound_range = ip_network(
2099 f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8", False
2101 dut1_remote_outbound_range = ip_network(
2102 f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8", False
2105 IPsecUtil.vpp_ipsec_add_spd_entry(
2109 PolicyAction.BYPASS,
2112 laddr_range=dut1_local_outbound_range,
2113 raddr_range=dut1_remote_outbound_range,
2115 IPsecUtil.vpp_ipsec_add_spd_entry(
2119 PolicyAction.BYPASS,
2122 laddr_range=dut1_remote_outbound_range,
2123 raddr_range=dut1_local_outbound_range,
2126 IPsecUtil.vpp_ipsec_add_sad_entries(
2140 IPsecUtil.vpp_ipsec_add_spd_entries(
2144 priority=ObjIncrement(p_lo, 0),
2145 action=PolicyAction.PROTECT,
2147 sa_id=ObjIncrement(sa_id_1, 1),
2148 raddr_range=NetworkIncrement(ip_network(raddr_ip2)),
2151 IPsecUtil.vpp_ipsec_add_sad_entries(
2164 IPsecUtil.vpp_ipsec_add_spd_entries(
2168 priority=ObjIncrement(p_lo, 0),
2169 action=PolicyAction.PROTECT,
2171 sa_id=ObjIncrement(sa_id_2, 1),
2172 raddr_range=NetworkIncrement(ip_network(raddr_ip1)),
2175 if "DUT2" in nodes.keys():
2176 rmac = Topology.get_interface_mac(nodes["DUT1"], interface1)
2177 IPsecUtil.vpp_ipsec_set_ip_route(
2188 IPsecUtil.vpp_ipsec_add_spd(nodes["DUT2"], spd_id)
2189 IPsecUtil.vpp_ipsec_spd_add_if(nodes["DUT2"], spd_id, interface2)
2190 for i in range(n_tunnels // (addr_incr**2) + 1):
2191 dut2_local_outbound_range = ip_network(
2192 f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8", False
2194 dut2_remote_outbound_range = ip_network(
2195 f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8", False
2198 IPsecUtil.vpp_ipsec_add_spd_entry(
2202 PolicyAction.BYPASS,
2205 laddr_range=dut2_remote_outbound_range,
2206 raddr_range=dut2_local_outbound_range,
2208 IPsecUtil.vpp_ipsec_add_spd_entry(
2212 PolicyAction.BYPASS,
2215 laddr_range=dut2_local_outbound_range,
2216 raddr_range=dut2_remote_outbound_range,
2219 IPsecUtil.vpp_ipsec_add_sad_entries(
2232 IPsecUtil.vpp_ipsec_add_spd_entries(
2236 priority=ObjIncrement(p_lo, 0),
2237 action=PolicyAction.PROTECT,
2239 sa_id=ObjIncrement(sa_id_1, 1),
2240 raddr_range=NetworkIncrement(ip_network(raddr_ip2)),
2243 IPsecUtil.vpp_ipsec_add_sad_entries(
2256 IPsecUtil.vpp_ipsec_add_spd_entries(
2260 priority=ObjIncrement(p_lo, 0),
2261 action=PolicyAction.PROTECT,
2263 sa_id=ObjIncrement(sa_id_2, 1),
2264 raddr_range=NetworkIncrement(ip_network(raddr_ip1)),
2268 def vpp_ipsec_show_all(node: dict) -> None:
2269 """Run "show ipsec all" debug CLI command.
2271 :param node: Node to run command on.
2274 PapiSocketExecutor.run_cli_cmd(node, "show ipsec all")
2277 def show_ipsec_security_association(node: dict) -> None:
2278 """Show IPSec security association.
2280 :param node: DUT node.
2283 cmd = "ipsec_sa_v5_dump"
2284 PapiSocketExecutor.dump_and_log(node, [cmd])
2287 def vpp_ipsec_flow_enable_rss(
2288 node: dict, proto: str, rss_type: str, function: str = "default"
2290 """Ipsec flow enable rss action.
2292 :param node: DUT node.
2293 :param proto: The flow protocol.
2294 :param rss_type: RSS type.
2295 :param function: RSS function.
2301 :returns: flow_index.
2304 # TODO: to be fixed to use full PAPI when it is ready in VPP
2306 f"test flow add src-ip any proto {proto} rss function"
2307 f" {function} rss types {rss_type}"
2309 stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2310 flow_index = stdout.split()[1]
2315 def vpp_create_ipsec_flows_on_dut(
2316 node: dict, n_flows: int, rx_queues: int, spi_start: int, interface: str
2318 """Create mutiple ipsec flows and enable flows onto interface.
2320 :param node: DUT node.
2321 :param n_flows: Number of flows to create.
2322 :param rx_queues: NUmber of RX queues.
2323 :param spi_start: The start spi.
2324 :param interface: Name of the interface.
2328 :type rx_queues: int
2329 :type spi_start: int
2330 :type interface: str
2333 for i in range(0, n_flows):
2334 rx_queue = i % rx_queues
2336 flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2337 node, "ESP", spi, "redirect-to-queue", value=rx_queue
2339 FlowUtil.vpp_flow_enable(node, interface, flow_index)