1 # Copyright (c) 2023 Cisco and/or its affiliates.
2 # Copyright (c) 2023 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."""
19 from enum import Enum, IntEnum
21 from ipaddress import ip_network, ip_address
22 from random import choice
23 from string import ascii_letters
25 from robot.libraries.BuiltIn import BuiltIn
27 from resources.libraries.python.Constants import Constants
28 from resources.libraries.python.IncrementUtil import ObjIncrement
29 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
31 from resources.libraries.python.IPAddress import IPAddress
32 from resources.libraries.python.IPUtil import IPUtil, IpDscp, \
33 MPLS_LABEL_INVALID, NetworkIncrement
34 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
35 from resources.libraries.python.ssh import scp_node
36 from resources.libraries.python.topology import Topology, NodeType
37 from resources.libraries.python.VPPUtil import VPPUtil
38 from resources.libraries.python.FlowUtil import FlowUtil
41 IPSEC_UDP_PORT_NONE = 0xffff
45 """Generate random string as a key.
47 :param length: Length of generated payload.
49 :returns: The generated payload.
53 choice(ascii_letters) for _ in range(length)
54 ).encode(encoding=u"utf-8")
57 class PolicyAction(Enum):
59 BYPASS = (u"bypass", 0)
60 DISCARD = (u"discard", 1)
61 PROTECT = (u"protect", 3)
63 def __init__(self, policy_name, policy_int_repr):
64 self.policy_name = policy_name
65 self.policy_int_repr = policy_int_repr
68 return self.policy_name
71 return self.policy_int_repr
74 class CryptoAlg(Enum):
75 """Encryption algorithms."""
76 AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
77 AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
78 AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
79 AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
81 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
82 self.alg_name = alg_name
83 self.alg_int_repr = alg_int_repr
84 self.scapy_name = scapy_name
85 self.key_len = key_len
89 """Integrity algorithm."""
90 SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
91 SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
93 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
94 self.alg_name = alg_name
95 self.alg_int_repr = alg_int_repr
96 self.scapy_name = scapy_name
97 self.key_len = key_len
100 class IPsecProto(IntEnum):
101 """IPsec protocol."""
102 IPSEC_API_PROTO_ESP = 50
103 IPSEC_API_PROTO_AH = 51
106 class IPsecSadFlags(IntEnum):
107 """IPsec Security Association Database flags."""
108 IPSEC_API_SAD_FLAG_NONE = 0
109 # Enable extended sequence numbers
110 IPSEC_API_SAD_FLAG_USE_ESN = 0x01
111 # Enable Anti - replay
112 IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02
113 # IPsec tunnel mode if non-zero, else transport mode
114 IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04
115 # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
116 # only valid if is_tunnel is non-zero
117 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08
118 # Enable UDP encapsulation for NAT traversal
119 IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10
120 # IPsec SA is or inbound traffic
121 IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
124 class TunnelEncpaDecapFlags(IntEnum):
125 """Flags controlling tunnel behaviour."""
126 TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
127 # at encap, copy the DF bit of the payload into the tunnel header
128 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
129 # at encap, set the DF bit in the tunnel header
130 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
131 # at encap, copy the DSCP bits of the payload into the tunnel header
132 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
133 # at encap, copy the ECN bit of the payload into the tunnel header
134 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
135 # at decap, copy the ECN bit of the tunnel header into the payload
136 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
139 class TunnelMode(IntEnum):
142 TUNNEL_API_MODE_P2P = 0
144 TUNNEL_API_MODE_MP = 1
148 """IPsec utilities."""
151 def policy_action_bypass():
152 """Return policy action bypass.
154 :returns: PolicyAction enum BYPASS object.
157 return PolicyAction.BYPASS
160 def policy_action_discard():
161 """Return policy action discard.
163 :returns: PolicyAction enum DISCARD object.
166 return PolicyAction.DISCARD
169 def policy_action_protect():
170 """Return policy action protect.
172 :returns: PolicyAction enum PROTECT object.
175 return PolicyAction.PROTECT
178 def crypto_alg_aes_cbc_128():
179 """Return encryption algorithm aes-cbc-128.
181 :returns: CryptoAlg enum AES_CBC_128 object.
184 return CryptoAlg.AES_CBC_128
187 def crypto_alg_aes_cbc_256():
188 """Return encryption algorithm aes-cbc-256.
190 :returns: CryptoAlg enum AES_CBC_256 object.
193 return CryptoAlg.AES_CBC_256
196 def crypto_alg_aes_gcm_128():
197 """Return encryption algorithm aes-gcm-128.
199 :returns: CryptoAlg enum AES_GCM_128 object.
202 return CryptoAlg.AES_GCM_128
205 def crypto_alg_aes_gcm_256():
206 """Return encryption algorithm aes-gcm-256.
208 :returns: CryptoAlg enum AES_GCM_128 object.
211 return CryptoAlg.AES_GCM_256
214 def get_crypto_alg_key_len(crypto_alg):
215 """Return encryption algorithm key length.
217 :param crypto_alg: Encryption algorithm.
218 :type crypto_alg: CryptoAlg
219 :returns: Key length.
222 return crypto_alg.key_len
225 def get_crypto_alg_scapy_name(crypto_alg):
226 """Return encryption algorithm scapy name.
228 :param crypto_alg: Encryption algorithm.
229 :type crypto_alg: CryptoAlg
230 :returns: Algorithm scapy name.
233 return crypto_alg.scapy_name
236 def integ_alg_sha_256_128():
237 """Return integrity algorithm SHA-256-128.
239 :returns: IntegAlg enum SHA_256_128 object.
242 return IntegAlg.SHA_256_128
245 def integ_alg_sha_512_256():
246 """Return integrity algorithm SHA-512-256.
248 :returns: IntegAlg enum SHA_512_256 object.
251 return IntegAlg.SHA_512_256
254 def get_integ_alg_key_len(integ_alg):
255 """Return integrity algorithm key length.
257 None argument is accepted, returning zero.
259 :param integ_alg: Integrity algorithm.
260 :type integ_alg: Optional[IntegAlg]
261 :returns: Key length.
264 return 0 if integ_alg is None else integ_alg.key_len
267 def get_integ_alg_scapy_name(integ_alg):
268 """Return integrity algorithm scapy name.
270 :param integ_alg: Integrity algorithm.
271 :type integ_alg: IntegAlg
272 :returns: Algorithm scapy name.
275 return integ_alg.scapy_name
278 def ipsec_proto_esp():
279 """Return IPSec protocol ESP.
281 :returns: IPsecProto enum ESP object.
284 return int(IPsecProto.IPSEC_API_PROTO_ESP)
287 def ipsec_proto_ah():
288 """Return IPSec protocol AH.
290 :returns: IPsecProto enum AH object.
293 return int(IPsecProto.IPSEC_API_PROTO_AH)
296 def vpp_ipsec_select_backend(node, protocol, index=1):
297 """Select IPsec backend.
299 :param node: VPP node to select IPsec backend on.
300 :param protocol: IPsec protocol.
301 :param index: Backend index.
303 :type protocol: IPsecProto
305 :raises RuntimeError: If failed to select IPsec backend or if no API
308 cmd = u"ipsec_select_backend"
309 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
314 with PapiSocketExecutor(node) as papi_exec:
315 papi_exec.add(cmd, **args).get_reply(err_msg)
318 def vpp_ipsec_set_async_mode(node, async_enable=1):
319 """Set IPsec async mode on|off.
321 :param node: VPP node to set IPsec async mode.
322 :param async_enable: Async mode on or off.
324 :type async_enable: int
325 :raises RuntimeError: If failed to set IPsec async mode or if no API
328 cmd = u"ipsec_set_async_mode"
329 err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
331 async_enable=async_enable
333 with PapiSocketExecutor(node) as papi_exec:
334 papi_exec.add(cmd, **args).get_reply(err_msg)
337 def vpp_ipsec_crypto_sw_scheduler_set_worker(
338 node, workers, crypto_enable=False):
339 """Enable or disable crypto on specific vpp worker threads.
341 :param node: VPP node to enable or disable crypto for worker threads.
342 :param workers: List of VPP thread numbers.
343 :param crypto_enable: Disable or enable crypto work.
345 :type workers: Iterable[int]
346 :type crypto_enable: bool
347 :raises RuntimeError: If failed to enable or disable crypto for worker
348 thread or if no API reply received.
350 for worker in workers:
351 cmd = u"crypto_sw_scheduler_set_worker"
352 err_msg = f"Failed to disable/enable crypto for worker thread " \
353 f"on host {node[u'host']}"
355 worker_index=worker - 1,
356 crypto_enable=crypto_enable
358 with PapiSocketExecutor(node) as papi_exec:
359 papi_exec.add(cmd, **args).get_reply(err_msg)
362 def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
363 nodes, crypto_enable=False):
364 """Enable or disable crypto on specific vpp worker threads.
366 :param node: VPP node to enable or disable crypto for worker threads.
367 :param crypto_enable: Disable or enable crypto work.
369 :type crypto_enable: bool
370 :raises RuntimeError: If failed to enable or disable crypto for worker
371 thread or if no API reply received.
373 for node_name, node in nodes.items():
374 if node["type"] == NodeType.DUT:
375 thread_data = VPPUtil.vpp_show_threads(node)
376 worker_cnt = len(thread_data) - 1
380 workers = BuiltIn().get_variable_value(
381 f"${{{node_name}_cpu_dp}}"
383 for item in thread_data:
384 if str(item.cpu_id) in workers.split(u","):
385 worker_ids.append(item.id)
387 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
388 node, workers=worker_ids, crypto_enable=crypto_enable
392 def vpp_ipsec_add_sad_entry(
393 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
394 integ_key=u"", tunnel_src=None, tunnel_dst=None):
395 """Create Security Association Database entry on the VPP node.
397 :param node: VPP node to add SAD entry on.
398 :param sad_id: SAD entry ID.
399 :param spi: Security Parameter Index of this SAD entry.
400 :param crypto_alg: The encryption algorithm name.
401 :param crypto_key: The encryption key string.
402 :param integ_alg: The integrity algorithm name.
403 :param integ_key: The integrity key string.
404 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
405 specified ESP transport mode is used.
406 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
407 not specified ESP transport mode is used.
411 :type crypto_alg: CryptoAlg
412 :type crypto_key: str
413 :type integ_alg: Optional[IntegAlg]
415 :type tunnel_src: str
416 :type tunnel_dst: str
418 if isinstance(crypto_key, str):
419 crypto_key = crypto_key.encode(encoding=u"utf-8")
420 if isinstance(integ_key, str):
421 integ_key = integ_key.encode(encoding=u"utf-8")
423 length=len(crypto_key),
427 length=len(integ_key),
428 data=integ_key if integ_key else 0
431 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
432 if tunnel_src and tunnel_dst:
433 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
434 src_addr = ip_address(tunnel_src)
435 dst_addr = ip_address(tunnel_dst)
436 if src_addr.version == 6:
438 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
443 cmd = u"ipsec_sad_entry_add"
444 err_msg = f"Failed to add Security Association Database entry " \
445 f"on host {node[u'host']}"
449 crypto_algorithm=crypto_alg.alg_int_repr,
451 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
458 encap_decap_flags=int(
459 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
461 dscp=int(IpDscp.IP_API_DSCP_CS0),
463 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
464 udp_src_port=4500, # default value in api
465 udp_dst_port=4500 # default value in api
467 args = dict(entry=sad_entry)
468 with PapiSocketExecutor(node) as papi_exec:
469 papi_exec.add(cmd, **args).get_reply(err_msg)
472 def vpp_ipsec_add_sad_entries(
473 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
474 integ_alg=None, integ_key=u"", tunnel_src=None,tunnel_dst=None,
475 tunnel_addr_incr=True):
476 """Create multiple Security Association Database entries on VPP node.
478 :param node: VPP node to add SAD entry on.
479 :param n_entries: Number of SAD entries to be created.
480 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
482 :param spi: Security Parameter Index of first SAD entry. All subsequent
483 SAD entries will have spi incremented by 1.
484 :param crypto_alg: The encryption algorithm name.
485 :param crypto_key: The encryption key string.
486 :param integ_alg: The integrity algorithm name.
487 :param integ_key: The integrity key string.
488 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
489 specified ESP transport mode is used.
490 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
491 not specified ESP transport mode is used.
492 :param tunnel_addr_incr: Enable or disable tunnel IP address
498 :type crypto_alg: CryptoAlg
499 :type crypto_key: str
500 :type integ_alg: Optional[IntegAlg]
502 :type tunnel_src: str
503 :type tunnel_dst: str
504 :type tunnel_addr_incr: bool
506 if isinstance(crypto_key, str):
507 crypto_key = crypto_key.encode(encoding=u"utf-8")
508 if isinstance(integ_key, str):
509 integ_key = integ_key.encode(encoding=u"utf-8")
510 if tunnel_src and tunnel_dst:
511 src_addr = ip_address(tunnel_src)
512 dst_addr = ip_address(tunnel_dst)
518 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
524 length=len(crypto_key),
528 length=len(integ_key),
529 data=integ_key if integ_key else 0
532 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
533 if tunnel_src and tunnel_dst:
534 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
535 if src_addr.version == 6:
537 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
540 cmd = u"ipsec_sad_entry_add"
541 err_msg = f"Failed to add Security Association Database entry " \
542 f"on host {node[u'host']}"
547 crypto_algorithm=crypto_alg.alg_int_repr,
549 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
556 encap_decap_flags=int(
557 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
559 dscp=int(IpDscp.IP_API_DSCP_CS0),
561 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
562 udp_src_port=4500, # default value in api
563 udp_dst_port=4500, # default value in api
565 args = dict(entry=sad_entry)
566 with PapiSocketExecutor(node, is_async=True) as papi_exec:
567 for i in range(n_entries):
568 args[u"entry"][u"sad_id"] = int(sad_id) + i
569 args[u"entry"][u"spi"] = int(spi) + i
570 args[u"entry"][u"tunnel"][u"src"] = (
571 str(src_addr + i * addr_incr)
572 if tunnel_src and tunnel_dst else src_addr
574 args[u"entry"][u"tunnel"][u"dst"] = (
575 str(dst_addr + i * addr_incr)
576 if tunnel_src and tunnel_dst else dst_addr
578 history = bool(not 1 < i < n_entries - 2)
579 papi_exec.add(cmd, history=history, **args)
580 papi_exec.get_replies(err_msg)
583 def vpp_ipsec_set_ip_route(
584 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
585 raddr_range, dst_mac=None):
586 """Set IP address and route on interface.
588 :param node: VPP node to add config on.
589 :param n_tunnels: Number of tunnels to create.
590 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
591 :param traffic_addr: Traffic destination IP address to route.
592 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
593 :param interface: Interface key on node 1.
594 :param raddr_range: Mask specifying range of Policy selector Remote IP
595 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
597 :param dst_mac: The MAC address of destination tunnels.
600 :type tunnel_src: str
601 :type traffic_addr: str
602 :type tunnel_dst: str
604 :type raddr_range: int
607 tunnel_src = ip_address(tunnel_src)
608 tunnel_dst = ip_address(tunnel_dst)
609 traffic_addr = ip_address(traffic_addr)
610 tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
611 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
612 else 1 << (32 - raddr_range)
614 cmd1 = u"sw_interface_add_del_address"
616 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
621 cmd2 = u"ip_route_add_del"
627 cmd3 = u"ip_neighbor_add_del"
631 sw_if_index=Topology.get_interface_sw_index(node, interface),
633 mac_address=str(dst_mac),
637 err_msg = f"Failed to configure IP addresses, IP routes and " \
638 f"IP neighbor on interface {interface} on host {node[u'host']}" \
640 else f"Failed to configure IP addresses and IP routes " \
641 f"on interface {interface} on host {node[u'host']}"
643 with PapiSocketExecutor(node, is_async=True) as papi_exec:
644 for i in range(n_tunnels):
645 tunnel_dst_addr = tunnel_dst + i * addr_incr
646 args1[u"prefix"] = IPUtil.create_prefix_object(
647 tunnel_src + i * addr_incr, raddr_range
649 args2[u"route"] = IPUtil.compose_vpp_route_structure(
650 node, traffic_addr + i,
651 prefix_len=tunnel_dst_prefix,
652 interface=interface, gateway=tunnel_dst_addr
654 history = bool(not 1 < i < n_tunnels - 2)
655 papi_exec.add(cmd1, history=history, **args1)
656 papi_exec.add(cmd2, history=history, **args2)
658 args2[u"route"] = IPUtil.compose_vpp_route_structure(
659 node, tunnel_dst_addr,
660 prefix_len=tunnel_dst_prefix,
661 interface=interface, gateway=tunnel_dst_addr
663 papi_exec.add(cmd2, history=history, **args2)
666 args3[u"neighbor"][u"ip_address"] = ip_address(
669 papi_exec.add(cmd3, history=history, **args3)
670 papi_exec.get_replies(err_msg)
673 def vpp_ipsec_add_spd(node, spd_id):
674 """Create Security Policy Database on the VPP node.
676 :param node: VPP node to add SPD on.
677 :param spd_id: SPD ID.
681 cmd = u"ipsec_spd_add_del"
682 err_msg = f"Failed to add Security Policy Database " \
683 f"on host {node[u'host']}"
688 with PapiSocketExecutor(node) as papi_exec:
689 papi_exec.add(cmd, **args).get_reply(err_msg)
692 def vpp_ipsec_spd_add_if(node, spd_id, interface):
693 """Add interface to the Security Policy Database.
695 :param node: VPP node.
696 :param spd_id: SPD ID to add interface on.
697 :param interface: Interface name or sw_if_index.
700 :type interface: str or int
702 cmd = u"ipsec_interface_add_del_spd"
703 err_msg = f"Failed to add interface {interface} to Security Policy " \
704 f"Database {spd_id} on host {node[u'host']}"
707 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
710 with PapiSocketExecutor(node) as papi_exec:
711 papi_exec.add(cmd, **args).get_reply(err_msg)
714 def vpp_ipsec_create_spds_match_nth_entry(
715 node, dir1_interface, dir2_interface, entry_amount,
716 local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
717 inbound=False, bidirectional=True):
718 """Create one matching SPD entry for inbound or outbound traffic on
719 a DUT for each traffic direction and also create entry_amount - 1
720 non-matching SPD entries. Create a Security Policy Database on each
721 outbound interface where these entries will be configured.
722 The matching SPD entry will have the lowest priority, input action and
723 will be configured to match the IP flow. The non-matching entries will
724 be the same, except with higher priority and non-matching IP flows.
726 Action Protect is currently not supported.
728 :param node: VPP node to configured the SPDs and their entries.
729 :param dir1_interface: The interface in direction 1 where the entries
731 :param dir2_interface: The interface in direction 2 where the entries
733 :param entry_amount: The number of SPD entries to configure. If
734 entry_amount == 1, no non-matching entries will be configured.
735 :param local_addr_range: Matching local address range in direction 1
736 in format IP/prefix or IP/mask. If no mask is provided, it's
737 considered to be /32.
738 :param remote_addr_range: Matching remote address range in
739 direction 1 in format IP/prefix or IP/mask. If no mask is
740 provided, it's considered to be /32.
741 :param action: Policy action.
742 :param inbound: If True policy is for inbound traffic, otherwise
744 :param bidirectional: When True, will create SPDs in both directions
745 of traffic. When False, only in one direction.
747 :type dir1_interface: Union[string, int]
748 :type dir2_interface: Union[string, int]
749 :type entry_amount: int
750 :type local_addr_range:
751 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
752 :type remote_addr_range:
753 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
754 :type action: IPsecUtil.PolicyAction
756 :type bidirectional: bool
757 :raises NotImplementedError: When the action is PolicyAction.PROTECT.
760 if action == PolicyAction.PROTECT:
761 raise NotImplementedError('Policy action PROTECT is not supported.')
765 matching_priority = 1
767 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
768 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
769 # matching entry direction 1
770 IPsecUtil.vpp_ipsec_add_spd_entry(
771 node, spd_id_dir1, matching_priority, action,
772 inbound=inbound, laddr_range=local_addr_range,
773 raddr_range=remote_addr_range
777 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
778 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
780 # matching entry direction 2, the address ranges are switched
781 IPsecUtil.vpp_ipsec_add_spd_entry(
782 node, spd_id_dir2, matching_priority, action,
783 inbound=inbound, laddr_range=remote_addr_range,
784 raddr_range=local_addr_range
787 # non-matching entries
788 no_match_entry_amount = entry_amount - 1
789 if no_match_entry_amount > 0:
790 # create a NetworkIncrement representation of the network,
791 # then skip the matching network
792 no_match_local_addr_range = NetworkIncrement(
793 ip_network(local_addr_range)
795 next(no_match_local_addr_range)
797 no_match_remote_addr_range = NetworkIncrement(
798 ip_network(remote_addr_range)
800 next(no_match_remote_addr_range)
802 # non-matching entries direction 1
803 IPsecUtil.vpp_ipsec_add_spd_entries(
804 node, no_match_entry_amount, spd_id_dir1,
805 ObjIncrement(matching_priority + 1, 1), action,
806 inbound=inbound, laddr_range=no_match_local_addr_range,
807 raddr_range=no_match_remote_addr_range
811 # reset the networks so that we're using a unified config
812 # the address ranges are switched
813 no_match_remote_addr_range = NetworkIncrement(
814 ip_network(local_addr_range)
816 next(no_match_remote_addr_range)
818 no_match_local_addr_range = NetworkIncrement(
819 ip_network(remote_addr_range)
821 next(no_match_local_addr_range)
822 # non-matching entries direction 2
823 IPsecUtil.vpp_ipsec_add_spd_entries(
824 node, no_match_entry_amount, spd_id_dir2,
825 ObjIncrement(matching_priority + 1, 1), action,
826 inbound=inbound, laddr_range=no_match_local_addr_range,
827 raddr_range=no_match_remote_addr_range
830 IPsecUtil.vpp_ipsec_show_all(node)
833 def _vpp_ipsec_add_spd_entry_internal(
834 executor, spd_id, priority, action, inbound=True, sa_id=None,
835 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
836 rport_range=None, is_ipv6=False):
837 """Prepare to create Security Policy Database entry on the VPP node.
839 This just adds one more command to the executor.
840 The call site shall get replies once all entries are added,
841 to get speed benefit from async PAPI.
843 :param executor: Open PAPI executor (async handling) to add commands to.
844 :param spd_id: SPD ID to add entry on.
845 :param priority: SPD entry priority, higher number = higher priority.
846 :param action: Policy action.
847 :param inbound: If True policy is for inbound traffic, otherwise
849 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
850 :param proto: Policy selector next layer protocol number.
851 :param laddr_range: Policy selector local IPv4 or IPv6 address range
852 in format IP/prefix or IP/mask. If no mask is provided,
853 it's considered to be /32.
854 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
855 in format IP/prefix or IP/mask. If no mask is provided,
856 it's considered to be /32.
857 :param lport_range: Policy selector local TCP/UDP port range in format
858 <port_start>-<port_end>.
859 :param rport_range: Policy selector remote TCP/UDP port range in format
860 <port_start>-<port_end>.
861 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
862 not defined so it will default to address ::/0, otherwise False.
863 :type executor: PapiSocketExecutor
866 :type action: IPsecUtil.PolicyAction
870 :type laddr_range: string
871 :type raddr_range: string
872 :type lport_range: string
873 :type rport_range: string
876 if laddr_range is None:
877 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
879 if raddr_range is None:
880 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
882 local_net = ip_network(laddr_range, strict=False)
883 remote_net = ip_network(raddr_range, strict=False)
885 cmd = u"ipsec_spd_entry_add_del_v2"
889 priority=int(priority),
890 is_outbound=not inbound,
891 sa_id=int(sa_id) if sa_id else 0,
893 protocol=255 if proto is None else int(proto),
894 remote_address_start=IPAddress.create_ip_address_object(
895 remote_net.network_address
897 remote_address_stop=IPAddress.create_ip_address_object(
898 remote_net.broadcast_address
900 local_address_start=IPAddress.create_ip_address_object(
901 local_net.network_address
903 local_address_stop=IPAddress.create_ip_address_object(
904 local_net.broadcast_address
906 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
908 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
910 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
912 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
919 executor.add(cmd, **args)
922 def vpp_ipsec_add_spd_entry(
923 node, spd_id, priority, action, inbound=True, sa_id=None,
924 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
925 rport_range=None, is_ipv6=False):
926 """Create Security Policy Database entry on the VPP node.
928 :param node: VPP node to add SPD entry on.
929 :param spd_id: SPD ID to add entry on.
930 :param priority: SPD entry priority, higher number = higher priority.
931 :param action: Policy action.
932 :param inbound: If True policy is for inbound traffic, otherwise
934 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
935 :param proto: Policy selector next layer protocol number.
936 :param laddr_range: Policy selector local IPv4 or IPv6 address range
937 in format IP/prefix or IP/mask. If no mask is provided,
938 it's considered to be /32.
939 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
940 in format IP/prefix or IP/mask. If no mask is provided,
941 it's considered to be /32.
942 :param lport_range: Policy selector local TCP/UDP port range in format
943 <port_start>-<port_end>.
944 :param rport_range: Policy selector remote TCP/UDP port range in format
945 <port_start>-<port_end>.
946 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
947 not defined so it will default to address ::/0, otherwise False.
951 :type action: IPsecUtil.PolicyAction
955 :type laddr_range: string
956 :type raddr_range: string
957 :type lport_range: string
958 :type rport_range: string
961 err_msg = f"Failed to add entry to Security Policy Database " \
962 f"{spd_id} on host {node[u'host']}"
963 with PapiSocketExecutor(node, is_async=True) as papi_exec:
964 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
965 papi_exec, spd_id, priority, action, inbound, sa_id, proto,
966 laddr_range, raddr_range, lport_range, rport_range, is_ipv6
968 papi_exec.get_replies(err_msg)
971 def vpp_ipsec_add_spd_entries(
972 node, n_entries, spd_id, priority, action, inbound, sa_id=None,
973 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
974 rport_range=None, is_ipv6=False):
975 """Create multiple Security Policy Database entries on the VPP node.
977 :param node: VPP node to add SPD entries on.
978 :param n_entries: Number of SPD entries to be added.
979 :param spd_id: SPD ID to add entries on.
980 :param priority: SPD entries priority, higher number = higher priority.
981 :param action: Policy action.
982 :param inbound: If True policy is for inbound traffic, otherwise
984 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
985 :param proto: Policy selector next layer protocol number.
986 :param laddr_range: Policy selector local IPv4 or IPv6 address range
987 in format IP/prefix or IP/mask. If no mask is provided,
988 it's considered to be /32.
989 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
990 in format IP/prefix or IP/mask. If no mask is provided,
991 it's considered to be /32.
992 :param lport_range: Policy selector local TCP/UDP port range in format
993 <port_start>-<port_end>.
994 :param rport_range: Policy selector remote TCP/UDP port range in format
995 <port_start>-<port_end>.
996 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
997 not defined so it will default to address ::/0, otherwise False.
1001 :type priority: IPsecUtil.ObjIncrement
1002 :type action: IPsecUtil.PolicyAction
1004 :type sa_id: IPsecUtil.ObjIncrement
1006 :type laddr_range: IPsecUtil.NetworkIncrement
1007 :type raddr_range: IPsecUtil.NetworkIncrement
1008 :type lport_range: string
1009 :type rport_range: string
1012 if laddr_range is None:
1013 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1014 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1016 if raddr_range is None:
1017 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1018 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1020 lport_range_start = 0
1021 lport_range_stop = 65535
1023 lport_range_start, lport_range_stop = lport_range.split('-')
1025 rport_range_start = 0
1026 rport_range_stop = 65535
1028 rport_range_start, rport_range_stop = rport_range.split('-')
1030 err_msg = f"Failed to add entry to Security Policy Database " \
1031 f"{spd_id} on host {node[u'host']}"
1032 with PapiSocketExecutor(node, is_async=True) as papi_exec:
1033 for _ in range(n_entries):
1034 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
1035 papi_exec, spd_id, next(priority), action, inbound,
1036 next(sa_id) if sa_id is not None else sa_id,
1037 proto, next(laddr_range), next(raddr_range), lport_range,
1038 rport_range, is_ipv6
1040 papi_exec.get_replies(err_msg)
1043 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1044 """Create loopback interface and set IP address on VPP node 1 interface
1047 :param nodes: VPP nodes to create tunnel interfaces.
1048 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1049 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1050 IPv4/IPv6 address (ip2).
1051 :param if1_key: VPP node 1 interface key from topology file.
1052 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1053 interface key from topology file.
1059 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1060 # Create loopback interface on DUT1, set it to up state
1061 cmd = u"create_loopback_instance"
1067 err_msg = f"Failed to create loopback interface " \
1068 f"on host {nodes[u'DUT1'][u'host']}"
1069 papi_exec.add(cmd, **args)
1070 loop_sw_if_idx = papi_exec.get_sw_if_index(err_msg)
1071 cmd = u"sw_interface_set_flags"
1073 sw_if_index=loop_sw_if_idx,
1074 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1076 err_msg = f"Failed to set loopback interface state up " \
1077 f"on host {nodes[u'DUT1'][u'host']}"
1078 papi_exec.add(cmd, **args).get_reply(err_msg)
1079 # Set IP address on VPP node 1 interface
1080 cmd = u"sw_interface_add_del_address"
1082 sw_if_index=InterfaceUtil.get_interface_index(
1083 nodes[u"DUT1"], if1_key
1087 prefix=IPUtil.create_prefix_object(
1088 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1092 err_msg = f"Failed to set IP address on interface {if1_key} " \
1093 f"on host {nodes[u'DUT1'][u'host']}"
1094 papi_exec.add(cmd, **args).get_reply(err_msg)
1095 cmd2 = u"ip_neighbor_add_del"
1099 sw_if_index=Topology.get_interface_sw_index(
1100 nodes[u"DUT1"], if1_key
1104 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1105 if u"DUT2" in nodes.keys()
1106 else Topology.get_interface_mac(
1107 nodes[u"TG"], if2_key
1110 ip_address=tun_ips[u"ip2"].compressed
1113 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1114 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1116 return loop_sw_if_idx
1119 def _ipsec_create_tunnel_interfaces_dut1_papi(
1120 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1121 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1122 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1124 Generate random keys and return them (so DUT2 or TG can decrypt).
1126 :param nodes: VPP nodes to create tunnel interfaces.
1127 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1128 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1129 IPv4/IPv6 address (ip2).
1130 :param if1_key: VPP node 1 interface key from topology file.
1131 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1132 interface key from topology file.
1133 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1134 :param crypto_alg: The encryption algorithm name.
1135 :param integ_alg: The integrity algorithm name.
1136 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1137 first tunnel in direction node2->node1.
1138 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1139 :param addr_incr: IP / IPv6 address incremental step.
1140 :param existing_tunnels: Number of tunnel interfaces before creation.
1141 Useful mainly for reconf tests. Default 0.
1146 :type n_tunnels: int
1147 :type crypto_alg: CryptoAlg
1148 :type integ_alg: Optional[IntegAlg]
1149 :type raddr_ip2: IPv4Address or IPv6Address
1150 :type addr_incr: int
1152 :type existing_tunnels: int
1153 :returns: Generated ckeys and ikeys.
1154 :rtype: List[bytes], List[bytes]
1156 if not existing_tunnels:
1157 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1158 nodes, tun_ips, if1_key, if2_key
1161 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1162 nodes[u"DUT1"], u"loop0"
1164 with PapiSocketExecutor(nodes[u"DUT1"], is_async=True) as papi_exec:
1165 # Configure IP addresses on loop0 interface
1166 cmd = u"sw_interface_add_del_address"
1168 sw_if_index=loop_sw_if_idx,
1173 for i in range(existing_tunnels, n_tunnels):
1174 args[u"prefix"] = IPUtil.create_prefix_object(
1175 tun_ips[u"ip1"] + i * addr_incr,
1176 128 if tun_ips[u"ip1"].version == 6 else 32
1179 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1181 # Configure IPIP tunnel interfaces
1182 cmd = u"ipip_add_tunnel"
1184 instance=Constants.BITWISE_NON_ZERO,
1189 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1191 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1192 dscp=int(IpDscp.IP_API_DSCP_CS0)
1197 ipip_tunnels = [None] * existing_tunnels
1198 for i in range(existing_tunnels, n_tunnels):
1199 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1200 tun_ips[u"ip1"] + i * addr_incr
1202 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1206 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1208 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1209 f" {nodes[u'DUT1'][u'host']}"
1210 ipip_tunnels.extend(
1212 reply[u"sw_if_index"]
1213 for reply in papi_exec.get_replies(err_msg)
1214 if u"sw_if_index" in reply
1217 # Configure IPSec SAD entries
1218 ckeys = [bytes()] * existing_tunnels
1219 ikeys = [bytes()] * existing_tunnels
1220 cmd = u"ipsec_sad_entry_add"
1232 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1233 crypto_algorithm=crypto_alg.alg_int_repr,
1235 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1236 integrity_key=i_key,
1242 encap_decap_flags=int(
1243 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1245 dscp=int(IpDscp.IP_API_DSCP_CS0),
1248 udp_src_port=IPSEC_UDP_PORT_NONE,
1249 udp_dst_port=IPSEC_UDP_PORT_NONE,
1251 args = dict(entry=sad_entry)
1252 for i in range(existing_tunnels, n_tunnels):
1254 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1257 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1259 # SAD entry for outband / tx path
1260 args[u"entry"][u"sad_id"] = i
1261 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1263 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1264 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1266 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1267 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1268 args[u"entry"][u"flags"] = int(
1269 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1272 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1274 # SAD entry for inband / rx path
1275 args[u"entry"][u"sad_id"] = 100000 + i
1276 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1278 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1279 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1281 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1282 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1283 args[u"entry"][u"flags"] = int(
1284 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1285 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1288 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1290 err_msg = f"Failed to add IPsec SAD entries on host" \
1291 f" {nodes[u'DUT1'][u'host']}"
1292 papi_exec.get_replies(err_msg)
1293 # Add protection for tunnels with IPSEC
1294 cmd = u"ipsec_tunnel_protect_update"
1297 via_label=MPLS_LABEL_INVALID,
1298 obj_id=Constants.BITWISE_NON_ZERO
1300 ipsec_tunnel_protect = dict(
1308 tunnel=ipsec_tunnel_protect
1310 for i in range(existing_tunnels, n_tunnels):
1311 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1312 args[u"tunnel"][u"sa_out"] = i
1313 args[u"tunnel"][u"sa_in"] = [100000 + i]
1315 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1317 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1318 f"on host {nodes[u'DUT1'][u'host']}"
1319 papi_exec.get_replies(err_msg)
1321 # Configure unnumbered interfaces
1322 cmd = u"sw_interface_set_unnumbered"
1325 sw_if_index=InterfaceUtil.get_interface_index(
1326 nodes[u"DUT1"], if1_key
1328 unnumbered_sw_if_index=0
1330 for i in range(existing_tunnels, n_tunnels):
1331 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1333 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1336 cmd = u"sw_interface_set_flags"
1339 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1341 for i in range(existing_tunnels, n_tunnels):
1342 args[u"sw_if_index"] = ipip_tunnels[i]
1344 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1346 # Configure IP routes
1347 cmd = u"ip_route_add_del"
1353 for i in range(existing_tunnels, n_tunnels):
1354 args[u"route"] = IPUtil.compose_vpp_route_structure(
1355 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1356 prefix_len=128 if raddr_ip2.version == 6 else 32,
1357 interface=ipip_tunnels[i]
1360 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1362 err_msg = f"Failed to add IP routes on host " \
1363 f"{nodes[u'DUT1'][u'host']}"
1364 papi_exec.get_replies(err_msg)
1369 def _ipsec_create_tunnel_interfaces_dut2_papi(
1370 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1371 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1372 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1374 This method accesses keys generated by DUT1 method
1375 and does not return anything.
1377 :param nodes: VPP nodes to create tunnel interfaces.
1378 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1379 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1380 IPv4/IPv6 address (ip2).
1381 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1382 interface key from topology file.
1383 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1384 :param crypto_alg: The encryption algorithm name.
1385 :param ckeys: List of encryption keys.
1386 :param integ_alg: The integrity algorithm name.
1387 :param ikeys: List of integrity keys.
1388 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1389 :param addr_incr: IP / IPv6 address incremental step.
1390 :param existing_tunnels: Number of tunnel interfaces before creation.
1391 Useful mainly for reconf tests. Default 0.
1395 :type n_tunnels: int
1396 :type crypto_alg: CryptoAlg
1397 :type ckeys: Sequence[bytes]
1398 :type integ_alg: Optional[IntegAlg]
1399 :type ikeys: Sequence[bytes]
1400 :type addr_incr: int
1402 :type existing_tunnels: int
1404 with PapiSocketExecutor(nodes[u"DUT2"], is_async=True) as papi_exec:
1405 if not existing_tunnels:
1406 # Set IP address on VPP node 2 interface
1407 cmd = u"sw_interface_add_del_address"
1409 sw_if_index=InterfaceUtil.get_interface_index(
1410 nodes[u"DUT2"], if2_key
1414 prefix=IPUtil.create_prefix_object(
1415 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1419 err_msg = f"Failed to set IP address on interface {if2_key} " \
1420 f"on host {nodes[u'DUT2'][u'host']}"
1421 papi_exec.add(cmd, **args).get_replies(err_msg)
1422 # Configure IPIP tunnel interfaces
1423 cmd = u"ipip_add_tunnel"
1425 instance=Constants.BITWISE_NON_ZERO,
1430 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1432 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1433 dscp=int(IpDscp.IP_API_DSCP_CS0)
1438 ipip_tunnels = [None] * existing_tunnels
1439 for i in range(existing_tunnels, n_tunnels):
1440 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1443 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1444 tun_ips[u"ip1"] + i * addr_incr
1447 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1449 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1450 f" {nodes[u'DUT2'][u'host']}"
1451 ipip_tunnels.extend(
1453 reply[u"sw_if_index"]
1454 for reply in papi_exec.get_replies(err_msg)
1455 if u"sw_if_index" in reply
1458 # Configure IPSec SAD entries
1459 cmd = u"ipsec_sad_entry_add"
1471 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1472 crypto_algorithm=crypto_alg.alg_int_repr,
1474 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1475 integrity_key=i_key,
1481 encap_decap_flags=int(
1482 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1484 dscp=int(IpDscp.IP_API_DSCP_CS0),
1487 udp_src_port=IPSEC_UDP_PORT_NONE,
1488 udp_dst_port=IPSEC_UDP_PORT_NONE,
1490 args = dict(entry=sad_entry)
1491 for i in range(existing_tunnels, n_tunnels):
1493 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1496 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1498 # SAD entry for outband / tx path
1499 args[u"entry"][u"sad_id"] = 100000 + i
1500 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1502 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1503 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1505 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1506 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1507 args[u"entry"][u"flags"] = int(
1508 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1511 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1513 # SAD entry for inband / rx path
1514 args[u"entry"][u"sad_id"] = i
1515 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1517 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1518 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1520 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1521 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1522 args[u"entry"][u"flags"] = int(
1523 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1524 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1527 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1529 err_msg = f"Failed to add IPsec SAD entries on host" \
1530 f" {nodes[u'DUT2'][u'host']}"
1531 papi_exec.get_replies(err_msg)
1532 # Add protection for tunnels with IPSEC
1533 cmd = u"ipsec_tunnel_protect_update"
1536 via_label=MPLS_LABEL_INVALID,
1537 obj_id=Constants.BITWISE_NON_ZERO
1539 ipsec_tunnel_protect = dict(
1547 tunnel=ipsec_tunnel_protect
1549 for i in range(existing_tunnels, n_tunnels):
1550 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1551 args[u"tunnel"][u"sa_out"] = 100000 + i
1552 args[u"tunnel"][u"sa_in"] = [i]
1554 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1556 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1557 f"on host {nodes[u'DUT2'][u'host']}"
1558 papi_exec.get_replies(err_msg)
1560 if not existing_tunnels:
1561 # Configure IP route
1562 cmd = u"ip_route_add_del"
1563 route = IPUtil.compose_vpp_route_structure(
1564 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1565 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1567 gateway=(tun_ips[u"ip2"] - 1).compressed
1574 papi_exec.add(cmd, **args)
1575 # Configure unnumbered interfaces
1576 cmd = u"sw_interface_set_unnumbered"
1579 sw_if_index=InterfaceUtil.get_interface_index(
1580 nodes[u"DUT2"], if2_key
1582 unnumbered_sw_if_index=0
1584 for i in range(existing_tunnels, n_tunnels):
1585 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1587 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1590 cmd = u"sw_interface_set_flags"
1593 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1595 for i in range(existing_tunnels, n_tunnels):
1596 args[u"sw_if_index"] = ipip_tunnels[i]
1598 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1600 # Configure IP routes
1601 cmd = u"ip_route_add_del"
1607 for i in range(existing_tunnels, n_tunnels):
1608 args[u"route"] = IPUtil.compose_vpp_route_structure(
1609 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1610 prefix_len=128 if raddr_ip1.version == 6 else 32,
1611 interface=ipip_tunnels[i]
1614 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1616 err_msg = f"Failed to add IP routes " \
1617 f"on host {nodes[u'DUT2'][u'host']}"
1618 papi_exec.get_replies(err_msg)
1621 def vpp_ipsec_create_tunnel_interfaces(
1622 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1623 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1624 existing_tunnels=0, return_keys=False):
1625 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1627 Some deployments (e.g. devicetest) need to know the generated keys.
1628 But other deployments (e.g. scale perf test) would get spammed
1629 if we returned keys every time.
1631 :param nodes: VPP nodes to create tunnel interfaces.
1632 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1634 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1636 :param if1_key: VPP node 1 interface key from topology file.
1637 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1638 interface key from topology file.
1639 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1640 :param crypto_alg: The encryption algorithm name.
1641 :param integ_alg: The integrity algorithm name.
1642 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1643 first tunnel in direction node1->node2.
1644 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1645 first tunnel in direction node2->node1.
1646 :param raddr_range: Mask specifying range of Policy selector Remote
1647 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1648 and to 128 in case of IPv6.
1649 :param existing_tunnels: Number of tunnel interfaces before creation.
1650 Useful mainly for reconf tests. Default 0.
1651 :param return_keys: Whether generated keys should be returned.
1653 :type tun_if1_ip_addr: str
1654 :type tun_if2_ip_addr: str
1657 :type n_tunnels: int
1658 :type crypto_alg: CryptoAlg
1659 :type integ_alg: Optonal[IntegAlg]
1660 :type raddr_ip1: string
1661 :type raddr_ip2: string
1662 :type raddr_range: int
1663 :type existing_tunnels: int
1664 :type return_keys: bool
1665 :returns: Ckeys, ikeys, spi_1, spi_2.
1666 :rtype: Optional[List[bytes], List[bytes], int, int]
1668 n_tunnels = int(n_tunnels)
1669 existing_tunnels = int(existing_tunnels)
1675 ip1=ip_address(tun_if1_ip_addr),
1676 ip2=ip_address(tun_if2_ip_addr)
1678 raddr_ip1 = ip_address(raddr_ip1)
1679 raddr_ip2 = ip_address(raddr_ip2)
1680 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1681 else 1 << (32 - raddr_range)
1683 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1684 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1685 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1687 if u"DUT2" in nodes.keys():
1688 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1689 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1690 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1695 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1699 def _create_ipsec_script_files(dut, instances):
1700 """Create script files for configuring IPsec in containers
1702 :param dut: DUT node on which to create the script files
1703 :param instances: number of containers on DUT node
1705 :type instances: int
1708 for cnf in range(0, instances):
1710 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1712 scripts.append(open(script_filename, 'w'))
1716 def _close_and_copy_ipsec_script_files(
1717 dut, nodes, instances, scripts):
1718 """Close created scripts and copy them to containers
1720 :param dut: DUT node on which to create the script files
1721 :param nodes: VPP nodes
1722 :param instances: number of containers on DUT node
1723 :param scripts: dictionary holding the script files
1726 :type instances: int
1729 for cnf in range(0, instances):
1730 scripts[cnf].close()
1732 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1734 scp_node(nodes[dut], script_filename, script_filename)
1738 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1739 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1740 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1741 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1743 :param nodes: VPP nodes to create tunnel interfaces.
1744 :param if1_ip_addr: VPP node 1 interface IP4 address.
1745 :param if2_ip_addr: VPP node 2 interface IP4 address.
1746 :param n_tunnels: Number of tunnell interfaces to create.
1747 :param crypto_alg: The encryption algorithm name.
1748 :param integ_alg: The integrity algorithm name.
1749 :param raddr_ip1: Policy selector remote IPv4 start address for the
1750 first tunnel in direction node1->node2.
1751 :param raddr_ip2: Policy selector remote IPv4 start address for the
1752 first tunnel in direction node2->node1.
1753 :param raddr_range: Mask specifying range of Policy selector Remote
1754 IPv4 addresses. Valid values are from 1 to 32.
1755 :param n_instances: Number of containers.
1757 :type if1_ip_addr: str
1758 :type if2_ip_addr: str
1759 :type n_tunnels: int
1760 :type crypto_alg: CryptoAlg
1761 :type integ_alg: Optional[IntegAlg]
1762 :type raddr_ip1: string
1763 :type raddr_ip2: string
1764 :type raddr_range: int
1765 :type n_instances: int
1769 addr_incr = 1 << (32 - raddr_range)
1771 dut1_scripts = IPsecUtil._create_ipsec_script_files(
1772 u"DUT1", n_instances
1774 dut2_scripts = IPsecUtil._create_ipsec_script_files(
1775 u"DUT2", n_instances
1778 for cnf in range(0, n_instances):
1779 dut1_scripts[cnf].write(
1780 u"create loopback interface\n"
1781 u"set interface state loop0 up\n\n"
1783 dut2_scripts[cnf].write(
1784 f"ip route add {if1_ip_addr}/8 via "
1785 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1788 for tnl in range(0, n_tunnels):
1789 cnf = tnl % n_instances
1791 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1795 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1799 f"integ-alg {integ_alg.alg_name} "
1800 f"local-integ-key {ikey} "
1801 f"remote-integ-key {ikey} "
1803 # Configure tunnel end point(s) on left side
1804 dut1_scripts[cnf].write(
1805 u"set interface ip address loop0 "
1806 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1807 f"create ipsec tunnel "
1808 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1809 f"local-spi {spi_1 + tnl} "
1810 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1811 f"remote-spi {spi_2 + tnl} "
1812 f"crypto-alg {crypto_alg.alg_name} "
1813 f"local-crypto-key {ckey} "
1814 f"remote-crypto-key {ckey} "
1815 f"instance {tnl // n_instances} "
1818 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1819 f"set interface state ipip{tnl // n_instances} up\n"
1820 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1821 f"via ipip{tnl // n_instances}\n\n"
1823 # Configure tunnel end point(s) on right side
1824 dut2_scripts[cnf].write(
1825 f"set ip neighbor memif1/{cnf + 1} "
1826 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1827 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1828 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1829 f"local-spi {spi_2 + tnl} "
1830 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1831 f"remote-spi {spi_1 + tnl} "
1832 f"crypto-alg {crypto_alg.alg_name} "
1833 f"local-crypto-key {ckey} "
1834 f"remote-crypto-key {ckey} "
1835 f"instance {tnl // n_instances} "
1838 f"set interface unnumbered ipip{tnl // n_instances} "
1839 f"use memif1/{cnf + 1}\n"
1840 f"set interface state ipip{tnl // n_instances} up\n"
1841 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1842 f"via ipip{tnl // n_instances}\n\n"
1845 IPsecUtil._close_and_copy_ipsec_script_files(
1846 u"DUT1", nodes, n_instances, dut1_scripts)
1847 IPsecUtil._close_and_copy_ipsec_script_files(
1848 u"DUT2", nodes, n_instances, dut2_scripts)
1851 def vpp_ipsec_add_multiple_tunnels(
1852 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1853 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range,
1854 tunnel_addr_incr=True):
1855 """Create multiple IPsec tunnels between two VPP nodes.
1857 :param nodes: VPP nodes to create tunnels.
1858 :param interface1: Interface name or sw_if_index on node 1.
1859 :param interface2: Interface name or sw_if_index on node 2.
1860 :param n_tunnels: Number of tunnels to create.
1861 :param crypto_alg: The encryption algorithm name.
1862 :param integ_alg: The integrity algorithm name.
1863 :param tunnel_ip1: Tunnel node1 IPv4 address.
1864 :param tunnel_ip2: Tunnel node2 IPv4 address.
1865 :param raddr_ip1: Policy selector remote IPv4 start address for the
1866 first tunnel in direction node1->node2.
1867 :param raddr_ip2: Policy selector remote IPv4 start address for the
1868 first tunnel in direction node2->node1.
1869 :param raddr_range: Mask specifying range of Policy selector Remote
1870 IPv4 addresses. Valid values are from 1 to 32.
1871 :param tunnel_addr_incr: Enable or disable tunnel IP address
1874 :type interface1: str or int
1875 :type interface2: str or int
1876 :type n_tunnels: int
1877 :type crypto_alg: CryptoAlg
1878 :type integ_alg: Optional[IntegAlg]
1879 :type tunnel_ip1: str
1880 :type tunnel_ip2: str
1881 :type raddr_ip1: string
1882 :type raddr_ip2: string
1883 :type raddr_range: int
1884 :type tunnel_addr_incr: bool
1894 crypto_key = gen_key(
1895 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1897 integ_key = gen_key(
1898 IPsecUtil.get_integ_alg_key_len(integ_alg)
1899 ).decode() if integ_alg else u""
1901 rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
1902 if u"DUT2" in nodes.keys() \
1903 else Topology.get_interface_mac(nodes[u"TG"], interface2)
1904 IPsecUtil.vpp_ipsec_set_ip_route(
1905 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1906 interface1, raddr_range, rmac)
1908 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1909 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1911 addr_incr = 1 << (128 - 96) if ip_address(tunnel_ip1).version == 6 \
1913 for i in range(n_tunnels//(addr_incr**2)+1):
1914 dut1_local_outbound_range = \
1915 ip_network(f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8",
1916 False).with_prefixlen
1917 dut1_remote_outbound_range = \
1918 ip_network(f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8",
1919 False).with_prefixlen
1921 IPsecUtil.vpp_ipsec_add_spd_entry(
1922 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1923 proto=50, laddr_range=dut1_local_outbound_range,
1924 raddr_range=dut1_remote_outbound_range
1926 IPsecUtil.vpp_ipsec_add_spd_entry(
1927 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1928 proto=50, laddr_range=dut1_remote_outbound_range,
1929 raddr_range=dut1_local_outbound_range
1932 IPsecUtil.vpp_ipsec_add_sad_entries(
1933 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1934 integ_alg, integ_key, tunnel_ip1, tunnel_ip2, tunnel_addr_incr
1937 IPsecUtil.vpp_ipsec_add_spd_entries(
1938 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
1939 action=PolicyAction.PROTECT, inbound=False,
1940 sa_id=ObjIncrement(sa_id_1, 1),
1941 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
1944 IPsecUtil.vpp_ipsec_add_sad_entries(
1945 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1946 integ_alg, integ_key, tunnel_ip2, tunnel_ip1, tunnel_addr_incr
1948 IPsecUtil.vpp_ipsec_add_spd_entries(
1949 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
1950 action=PolicyAction.PROTECT, inbound=True,
1951 sa_id=ObjIncrement(sa_id_2, 1),
1952 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
1955 if u"DUT2" in nodes.keys():
1956 rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
1957 IPsecUtil.vpp_ipsec_set_ip_route(
1958 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1959 interface2, raddr_range, rmac)
1961 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1962 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1963 for i in range(n_tunnels//(addr_incr**2)+1):
1964 dut2_local_outbound_range = \
1965 ip_network(f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8",
1966 False).with_prefixlen
1967 dut2_remote_outbound_range = \
1968 ip_network(f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8",
1969 False).with_prefixlen
1971 IPsecUtil.vpp_ipsec_add_spd_entry(
1972 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
1973 inbound=False, proto=50, laddr_range=dut2_remote_outbound_range,
1974 raddr_range=dut2_local_outbound_range
1976 IPsecUtil.vpp_ipsec_add_spd_entry(
1977 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
1978 inbound=True, proto=50, laddr_range=dut2_local_outbound_range,
1979 raddr_range=dut2_remote_outbound_range
1982 IPsecUtil.vpp_ipsec_add_sad_entries(
1983 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
1984 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2,
1987 IPsecUtil.vpp_ipsec_add_spd_entries(
1988 nodes[u"DUT2"], n_tunnels, spd_id,
1989 priority=ObjIncrement(p_lo, 0),
1990 action=PolicyAction.PROTECT, inbound=True,
1991 sa_id=ObjIncrement(sa_id_1, 1),
1992 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
1995 IPsecUtil.vpp_ipsec_add_sad_entries(
1996 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
1997 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1,
2000 IPsecUtil.vpp_ipsec_add_spd_entries(
2001 nodes[u"DUT2"], n_tunnels, spd_id,
2002 priority=ObjIncrement(p_lo, 0),
2003 action=PolicyAction.PROTECT, inbound=False,
2004 sa_id=ObjIncrement(sa_id_2, 1),
2005 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2009 def vpp_ipsec_show_all(node):
2010 """Run "show ipsec all" debug CLI command.
2012 :param node: Node to run command on.
2015 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2018 def show_ipsec_security_association(node):
2019 """Show IPSec security association.
2021 :param node: DUT node.
2027 PapiSocketExecutor.dump_and_log(node, cmds)
2030 def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2031 """Ipsec flow enable rss action.
2033 :param node: DUT node.
2034 :param proto: The flow protocol.
2035 :param type: RSS type.
2036 :param function: RSS function.
2042 :returns: flow_index.
2044 # TODO: to be fixed to use full PAPI when it is ready in VPP
2045 cmd = f"test flow add src-ip any proto {proto} rss function " \
2046 f"{function} rss types {type}"
2047 stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2048 flow_index = stdout.split()[1]
2053 def vpp_create_ipsec_flows_on_dut(
2054 node, n_flows, rx_queues, spi_start, interface):
2055 """Create mutiple ipsec flows and enable flows onto interface.
2057 :param node: DUT node.
2058 :param n_flows: Number of flows to create.
2059 :param rx_queues: NUmber of RX queues.
2060 :param spi_start: The start spi.
2061 :param interface: Name of the interface.
2065 :type rx_queues: int
2066 :type spi_start: int
2067 :type interface: str
2068 :returns: flow_index.
2071 for i in range(0, n_flows):
2072 rx_queue = i%rx_queues
2075 flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2076 node, "ESP", spi, "redirect-to-queue", value=rx_queue)
2077 FlowUtil.vpp_flow_enable(node, interface, flow_index)