feat(core): Core allocation
[csit.git] / resources / libraries / python / IPsecUtil.py
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:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 """IPsec utilities library."""
16
17 import os
18
19 from enum import Enum, IntEnum
20 from io import open
21 from ipaddress import ip_network, ip_address
22 from random import choice
23 from string import ascii_letters
24
25 from robot.libraries.BuiltIn import BuiltIn
26
27 from resources.libraries.python.Constants import Constants
28 from resources.libraries.python.IncrementUtil import ObjIncrement
29 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
30     InterfaceStatusFlags
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
39
40
41 IPSEC_UDP_PORT_NONE = 0xffff
42
43
44 def gen_key(length):
45     """Generate random string as a key.
46
47     :param length: Length of generated payload.
48     :type length: int
49     :returns: The generated payload.
50     :rtype: bytes
51     """
52     return u"".join(
53         choice(ascii_letters) for _ in range(length)
54     ).encode(encoding=u"utf-8")
55
56
57 class PolicyAction(Enum):
58     """Policy actions."""
59     BYPASS = (u"bypass", 0)
60     DISCARD = (u"discard", 1)
61     PROTECT = (u"protect", 3)
62
63     def __init__(self, policy_name, policy_int_repr):
64         self.policy_name = policy_name
65         self.policy_int_repr = policy_int_repr
66
67     def __str__(self):
68         return self.policy_name
69
70     def __int__(self):
71         return self.policy_int_repr
72
73
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)
80
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
86
87
88 class IntegAlg(Enum):
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)
92
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
98
99
100 class IPsecProto(IntEnum):
101     """IPsec protocol."""
102     IPSEC_API_PROTO_ESP = 50
103     IPSEC_API_PROTO_AH = 51
104
105
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
122
123
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
137
138
139 class TunnelMode(IntEnum):
140     """Tunnel modes."""
141     # point-to-point
142     TUNNEL_API_MODE_P2P = 0
143     # multi-point
144     TUNNEL_API_MODE_MP = 1
145
146
147 class IPsecUtil:
148     """IPsec utilities."""
149
150     @staticmethod
151     def policy_action_bypass():
152         """Return policy action bypass.
153
154         :returns: PolicyAction enum BYPASS object.
155         :rtype: PolicyAction
156         """
157         return PolicyAction.BYPASS
158
159     @staticmethod
160     def policy_action_discard():
161         """Return policy action discard.
162
163         :returns: PolicyAction enum DISCARD object.
164         :rtype: PolicyAction
165         """
166         return PolicyAction.DISCARD
167
168     @staticmethod
169     def policy_action_protect():
170         """Return policy action protect.
171
172         :returns: PolicyAction enum PROTECT object.
173         :rtype: PolicyAction
174         """
175         return PolicyAction.PROTECT
176
177     @staticmethod
178     def crypto_alg_aes_cbc_128():
179         """Return encryption algorithm aes-cbc-128.
180
181         :returns: CryptoAlg enum AES_CBC_128 object.
182         :rtype: CryptoAlg
183         """
184         return CryptoAlg.AES_CBC_128
185
186     @staticmethod
187     def crypto_alg_aes_cbc_256():
188         """Return encryption algorithm aes-cbc-256.
189
190         :returns: CryptoAlg enum AES_CBC_256 object.
191         :rtype: CryptoAlg
192         """
193         return CryptoAlg.AES_CBC_256
194
195     @staticmethod
196     def crypto_alg_aes_gcm_128():
197         """Return encryption algorithm aes-gcm-128.
198
199         :returns: CryptoAlg enum AES_GCM_128 object.
200         :rtype: CryptoAlg
201         """
202         return CryptoAlg.AES_GCM_128
203
204     @staticmethod
205     def crypto_alg_aes_gcm_256():
206         """Return encryption algorithm aes-gcm-256.
207
208         :returns: CryptoAlg enum AES_GCM_128 object.
209         :rtype: CryptoAlg
210         """
211         return CryptoAlg.AES_GCM_256
212
213     @staticmethod
214     def get_crypto_alg_key_len(crypto_alg):
215         """Return encryption algorithm key length.
216
217         :param crypto_alg: Encryption algorithm.
218         :type crypto_alg: CryptoAlg
219         :returns: Key length.
220         :rtype: int
221         """
222         return crypto_alg.key_len
223
224     @staticmethod
225     def get_crypto_alg_scapy_name(crypto_alg):
226         """Return encryption algorithm scapy name.
227
228         :param crypto_alg: Encryption algorithm.
229         :type crypto_alg: CryptoAlg
230         :returns: Algorithm scapy name.
231         :rtype: str
232         """
233         return crypto_alg.scapy_name
234
235     @staticmethod
236     def integ_alg_sha_256_128():
237         """Return integrity algorithm SHA-256-128.
238
239         :returns: IntegAlg enum SHA_256_128 object.
240         :rtype: IntegAlg
241         """
242         return IntegAlg.SHA_256_128
243
244     @staticmethod
245     def integ_alg_sha_512_256():
246         """Return integrity algorithm SHA-512-256.
247
248         :returns: IntegAlg enum SHA_512_256 object.
249         :rtype: IntegAlg
250         """
251         return IntegAlg.SHA_512_256
252
253     @staticmethod
254     def get_integ_alg_key_len(integ_alg):
255         """Return integrity algorithm key length.
256
257         None argument is accepted, returning zero.
258
259         :param integ_alg: Integrity algorithm.
260         :type integ_alg: Optional[IntegAlg]
261         :returns: Key length.
262         :rtype: int
263         """
264         return 0 if integ_alg is None else integ_alg.key_len
265
266     @staticmethod
267     def get_integ_alg_scapy_name(integ_alg):
268         """Return integrity algorithm scapy name.
269
270         :param integ_alg: Integrity algorithm.
271         :type integ_alg: IntegAlg
272         :returns: Algorithm scapy name.
273         :rtype: str
274         """
275         return integ_alg.scapy_name
276
277     @staticmethod
278     def ipsec_proto_esp():
279         """Return IPSec protocol ESP.
280
281         :returns: IPsecProto enum ESP object.
282         :rtype: IPsecProto
283         """
284         return int(IPsecProto.IPSEC_API_PROTO_ESP)
285
286     @staticmethod
287     def ipsec_proto_ah():
288         """Return IPSec protocol AH.
289
290         :returns: IPsecProto enum AH object.
291         :rtype: IPsecProto
292         """
293         return int(IPsecProto.IPSEC_API_PROTO_AH)
294
295     @staticmethod
296     def vpp_ipsec_select_backend(node, protocol, index=1):
297         """Select IPsec backend.
298
299         :param node: VPP node to select IPsec backend on.
300         :param protocol: IPsec protocol.
301         :param index: Backend index.
302         :type node: dict
303         :type protocol: IPsecProto
304         :type index: int
305         :raises RuntimeError: If failed to select IPsec backend or if no API
306             reply received.
307         """
308         cmd = u"ipsec_select_backend"
309         err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
310         args = dict(
311             protocol=protocol,
312             index=index
313         )
314         with PapiSocketExecutor(node) as papi_exec:
315             papi_exec.add(cmd, **args).get_reply(err_msg)
316
317     @staticmethod
318     def vpp_ipsec_set_async_mode(node, async_enable=1):
319         """Set IPsec async mode on|off.
320
321         :param node: VPP node to set IPsec async mode.
322         :param async_enable: Async mode on or off.
323         :type node: dict
324         :type async_enable: int
325         :raises RuntimeError: If failed to set IPsec async mode or if no API
326             reply received.
327         """
328         cmd = u"ipsec_set_async_mode"
329         err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
330         args = dict(
331             async_enable=async_enable
332         )
333         with PapiSocketExecutor(node) as papi_exec:
334             papi_exec.add(cmd, **args).get_reply(err_msg)
335
336     @staticmethod
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.
340
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.
344         :type node: dict
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.
349         """
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']}"
354             args = dict(
355                 worker_index=worker - 1,
356                 crypto_enable=crypto_enable
357             )
358             with PapiSocketExecutor(node) as papi_exec:
359                 papi_exec.add(cmd, **args).get_reply(err_msg)
360
361     @staticmethod
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.
365
366         :param node: VPP node to enable or disable crypto for worker threads.
367         :param crypto_enable: Disable or enable crypto work.
368         :type node: dict
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.
372         """
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
377                 if not worker_cnt:
378                     return None
379                 worker_ids = list()
380                 workers = BuiltIn().get_variable_value(
381                     f"${{{node_name}_cpu_dp}}"
382                 )
383                 for item in thread_data:
384                     if str(item.cpu_id) in workers.split(u","):
385                         worker_ids.append(item.id)
386
387                 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
388                     node, workers=worker_ids, crypto_enable=crypto_enable
389                 )
390
391     @staticmethod
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.
396
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.
408         :type node: dict
409         :type sad_id: int
410         :type spi: int
411         :type crypto_alg: CryptoAlg
412         :type crypto_key: str
413         :type integ_alg: Optional[IntegAlg]
414         :type integ_key: str
415         :type tunnel_src: str
416         :type tunnel_dst: str
417         """
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")
422         ckey = dict(
423             length=len(crypto_key),
424             data=crypto_key
425         )
426         ikey = dict(
427             length=len(integ_key),
428             data=integ_key if integ_key else 0
429         )
430
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:
437                 flags = \
438                     flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
439         else:
440             src_addr = u""
441             dst_addr = u""
442
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']}"
446         sad_entry = dict(
447             sad_id=int(sad_id),
448             spi=int(spi),
449             crypto_algorithm=crypto_alg.alg_int_repr,
450             crypto_key=ckey,
451             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
452             integrity_key=ikey,
453             flags=flags,
454             tunnel=dict(
455                 src=str(src_addr),
456                 dst=str(dst_addr),
457                 table_id=0,
458                 encap_decap_flags=int(
459                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
460                 ),
461                 dscp=int(IpDscp.IP_API_DSCP_CS0),
462             ),
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
466         )
467         args = dict(entry=sad_entry)
468         with PapiSocketExecutor(node) as papi_exec:
469             papi_exec.add(cmd, **args).get_reply(err_msg)
470
471     @staticmethod
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.
477
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
481             id incremented by 1.
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
493             incremental step.
494         :type node: dict
495         :type n_entries: int
496         :type sad_id: int
497         :type spi: int
498         :type crypto_alg: CryptoAlg
499         :type crypto_key: str
500         :type integ_alg: Optional[IntegAlg]
501         :type integ_key: str
502         :type tunnel_src: str
503         :type tunnel_dst: str
504         :type tunnel_addr_incr: bool
505         """
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)
513         else:
514             src_addr = u""
515             dst_addr = u""
516
517         if tunnel_addr_incr:
518             addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
519                 else 1 << (32 - 24)
520         else:
521             addr_incr = 0
522
523         ckey = dict(
524             length=len(crypto_key),
525             data=crypto_key
526         )
527         ikey = dict(
528             length=len(integ_key),
529             data=integ_key if integ_key else 0
530         )
531
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:
536                 flags = flags | int(
537                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
538                 )
539
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']}"
543
544         sad_entry = dict(
545             sad_id=int(sad_id),
546             spi=int(spi),
547             crypto_algorithm=crypto_alg.alg_int_repr,
548             crypto_key=ckey,
549             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
550             integrity_key=ikey,
551             flags=flags,
552             tunnel=dict(
553                 src=str(src_addr),
554                 dst=str(dst_addr),
555                 table_id=0,
556                 encap_decap_flags=int(
557                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
558                 ),
559                 dscp=int(IpDscp.IP_API_DSCP_CS0),
560             ),
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
564         )
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
573                 )
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
577                 )
578                 history = bool(not 1 < i < n_entries - 2)
579                 papi_exec.add(cmd, history=history, **args)
580             papi_exec.get_replies(err_msg)
581
582     @staticmethod
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.
587
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
596             in case of IPv6.
597         :param dst_mac: The MAC address of destination tunnels.
598         :type node: dict
599         :type n_tunnels: int
600         :type tunnel_src: str
601         :type traffic_addr: str
602         :type tunnel_dst: str
603         :type interface: str
604         :type raddr_range: int
605         :type dst_mac: str
606         """
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)
613
614         cmd1 = u"sw_interface_add_del_address"
615         args1 = dict(
616             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
617             is_add=True,
618             del_all=False,
619             prefix=None
620         )
621         cmd2 = u"ip_route_add_del"
622         args2 = dict(
623             is_add=1,
624             is_multipath=0,
625             route=None
626         )
627         cmd3 = u"ip_neighbor_add_del"
628         args3 = dict(
629             is_add=True,
630             neighbor=dict(
631                 sw_if_index=Topology.get_interface_sw_index(node, interface),
632                 flags=0,
633                 mac_address=str(dst_mac),
634                 ip_address=None
635             )
636         )
637         err_msg = f"Failed to configure IP addresses, IP routes and " \
638             f"IP neighbor on interface {interface} on host {node[u'host']}" \
639             if dst_mac \
640             else f"Failed to configure IP addresses and IP routes " \
641                  f"on interface {interface} on host {node[u'host']}"
642
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
648                 )
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
653                 )
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)
657
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
662                 )
663                 papi_exec.add(cmd2, history=history, **args2)
664
665                 if dst_mac:
666                     args3[u"neighbor"][u"ip_address"] = ip_address(
667                         tunnel_dst_addr
668                     )
669                     papi_exec.add(cmd3, history=history, **args3)
670             papi_exec.get_replies(err_msg)
671
672     @staticmethod
673     def vpp_ipsec_add_spd(node, spd_id):
674         """Create Security Policy Database on the VPP node.
675
676         :param node: VPP node to add SPD on.
677         :param spd_id: SPD ID.
678         :type node: dict
679         :type spd_id: int
680         """
681         cmd = u"ipsec_spd_add_del"
682         err_msg = f"Failed to add Security Policy Database " \
683             f"on host {node[u'host']}"
684         args = dict(
685             is_add=True,
686             spd_id=int(spd_id)
687         )
688         with PapiSocketExecutor(node) as papi_exec:
689             papi_exec.add(cmd, **args).get_reply(err_msg)
690
691     @staticmethod
692     def vpp_ipsec_spd_add_if(node, spd_id, interface):
693         """Add interface to the Security Policy Database.
694
695         :param node: VPP node.
696         :param spd_id: SPD ID to add interface on.
697         :param interface: Interface name or sw_if_index.
698         :type node: dict
699         :type spd_id: int
700         :type interface: str or int
701         """
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']}"
705         args = dict(
706             is_add=True,
707             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
708             spd_id=int(spd_id)
709         )
710         with PapiSocketExecutor(node) as papi_exec:
711             papi_exec.add(cmd, **args).get_reply(err_msg)
712
713     @staticmethod
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.
725
726         Action Protect is currently not supported.
727
728         :param node: VPP node to configured the SPDs and their entries.
729         :param dir1_interface: The interface in direction 1 where the entries
730             will be checked.
731         :param dir2_interface: The interface in direction 2 where the entries
732             will be checked.
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
743             outbound.
744         :param bidirectional: When True, will create SPDs in both directions
745             of traffic. When False, only in one direction.
746         :type node: dict
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
755         :type inbound: bool
756         :type bidirectional: bool
757         :raises NotImplementedError: When the action is PolicyAction.PROTECT.
758         """
759
760         if action == PolicyAction.PROTECT:
761             raise NotImplementedError('Policy action PROTECT is not supported.')
762
763         spd_id_dir1 = 1
764         spd_id_dir2 = 2
765         matching_priority = 1
766
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
774         )
775
776         if bidirectional:
777             IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
778             IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
779
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
785             )
786
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)
794             )
795             next(no_match_local_addr_range)
796
797             no_match_remote_addr_range = NetworkIncrement(
798                 ip_network(remote_addr_range)
799             )
800             next(no_match_remote_addr_range)
801
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
808             )
809
810             if bidirectional:
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)
815                 )
816                 next(no_match_remote_addr_range)
817
818                 no_match_local_addr_range = NetworkIncrement(
819                     ip_network(remote_addr_range)
820                 )
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
828                 )
829
830         IPsecUtil.vpp_ipsec_show_all(node)
831
832     @staticmethod
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.
838
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.
842
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
848             outbound.
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
864         :type spd_id: int
865         :type priority: int
866         :type action: IPsecUtil.PolicyAction
867         :type inbound: bool
868         :type sa_id: int
869         :type proto: int
870         :type laddr_range: string
871         :type raddr_range: string
872         :type lport_range: string
873         :type rport_range: string
874         :type is_ipv6: bool
875         """
876         if laddr_range is None:
877             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
878
879         if raddr_range is None:
880             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
881
882         local_net = ip_network(laddr_range, strict=False)
883         remote_net = ip_network(raddr_range, strict=False)
884
885         cmd = u"ipsec_spd_entry_add_del_v2"
886
887         spd_entry = dict(
888             spd_id=int(spd_id),
889             priority=int(priority),
890             is_outbound=not inbound,
891             sa_id=int(sa_id) if sa_id else 0,
892             policy=int(action),
893             protocol=255 if proto is None else int(proto),
894             remote_address_start=IPAddress.create_ip_address_object(
895                 remote_net.network_address
896             ),
897             remote_address_stop=IPAddress.create_ip_address_object(
898                 remote_net.broadcast_address
899             ),
900             local_address_start=IPAddress.create_ip_address_object(
901                 local_net.network_address
902             ),
903             local_address_stop=IPAddress.create_ip_address_object(
904                 local_net.broadcast_address
905             ),
906             remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
907             else 0,
908             remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
909             else 65535,
910             local_port_start=int(lport_range.split(u"-")[0]) if lport_range
911             else 0,
912             local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
913             else 65535
914         )
915         args = dict(
916             is_add=True,
917             entry=spd_entry
918         )
919         executor.add(cmd, **args)
920
921     @staticmethod
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.
927
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
933             outbound.
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.
948         :type node: dict
949         :type spd_id: int
950         :type priority: int
951         :type action: IPsecUtil.PolicyAction
952         :type inbound: bool
953         :type sa_id: int
954         :type proto: int
955         :type laddr_range: string
956         :type raddr_range: string
957         :type lport_range: string
958         :type rport_range: string
959         :type is_ipv6: bool
960         """
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
967             )
968             papi_exec.get_replies(err_msg)
969
970     @staticmethod
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.
976
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
983             outbound.
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.
998         :type node: dict
999         :type n_entries: int
1000         :type spd_id: int
1001         :type priority: IPsecUtil.ObjIncrement
1002         :type action: IPsecUtil.PolicyAction
1003         :type inbound: bool
1004         :type sa_id: IPsecUtil.ObjIncrement
1005         :type proto: int
1006         :type laddr_range: IPsecUtil.NetworkIncrement
1007         :type raddr_range: IPsecUtil.NetworkIncrement
1008         :type lport_range: string
1009         :type rport_range: string
1010         :type is_ipv6: bool
1011         """
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)
1015
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)
1019
1020         lport_range_start = 0
1021         lport_range_stop = 65535
1022         if lport_range:
1023             lport_range_start, lport_range_stop = lport_range.split('-')
1024
1025         rport_range_start = 0
1026         rport_range_stop = 65535
1027         if rport_range:
1028             rport_range_start, rport_range_stop = rport_range.split('-')
1029
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
1039                 )
1040             papi_exec.get_replies(err_msg)
1041
1042     @staticmethod
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
1045         using PAPI.
1046
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.
1054         :type nodes: dict
1055         :type tun_ips: dict
1056         :type if1_key: str
1057         :type if2_key: str
1058         """
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"
1062             args = dict(
1063                 mac_address=0,
1064                 is_specified=False,
1065                 user_instance=0,
1066             )
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"
1072             args = dict(
1073                 sw_if_index=loop_sw_if_idx,
1074                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1075             )
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"
1081             args = dict(
1082                 sw_if_index=InterfaceUtil.get_interface_index(
1083                     nodes[u"DUT1"], if1_key
1084                 ),
1085                 is_add=True,
1086                 del_all=False,
1087                 prefix=IPUtil.create_prefix_object(
1088                     tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1089                     else 24
1090                 )
1091             )
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"
1096             args2 = dict(
1097                 is_add=1,
1098                 neighbor=dict(
1099                     sw_if_index=Topology.get_interface_sw_index(
1100                         nodes[u"DUT1"], if1_key
1101                     ),
1102                     flags=1,
1103                     mac_address=str(
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
1108                         )
1109                     ),
1110                     ip_address=tun_ips[u"ip2"].compressed
1111                 )
1112             )
1113             err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1114             papi_exec.add(cmd2, **args2).get_reply(err_msg)
1115
1116             return loop_sw_if_idx
1117
1118     @staticmethod
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.
1123
1124         Generate random keys and return them (so DUT2 or TG can decrypt).
1125
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.
1142         :type nodes: dict
1143         :type tun_ips: dict
1144         :type if1_key: str
1145         :type if2_key: str
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
1151         :type spi_d: dict
1152         :type existing_tunnels: int
1153         :returns: Generated ckeys and ikeys.
1154         :rtype: List[bytes], List[bytes]
1155         """
1156         if not existing_tunnels:
1157             loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1158                 nodes, tun_ips, if1_key, if2_key
1159             )
1160         else:
1161             loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1162                 nodes[u"DUT1"], u"loop0"
1163             )
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"
1167             args = dict(
1168                 sw_if_index=loop_sw_if_idx,
1169                 is_add=True,
1170                 del_all=False,
1171                 prefix=None
1172             )
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
1177                 )
1178                 papi_exec.add(
1179                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1180                 )
1181             # Configure IPIP tunnel interfaces
1182             cmd = u"ipip_add_tunnel"
1183             ipip_tunnel = dict(
1184                 instance=Constants.BITWISE_NON_ZERO,
1185                 src=None,
1186                 dst=None,
1187                 table_id=0,
1188                 flags=int(
1189                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1190                 ),
1191                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1192                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1193             )
1194             args = dict(
1195                 tunnel=ipip_tunnel
1196             )
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
1201                 )
1202                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1203                     tun_ips[u"ip2"]
1204                 )
1205                 papi_exec.add(
1206                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1207                 )
1208             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1209                 f" {nodes[u'DUT1'][u'host']}"
1210             ipip_tunnels.extend(
1211                 [
1212                     reply[u"sw_if_index"]
1213                     for reply in papi_exec.get_replies(err_msg)
1214                     if u"sw_if_index" in reply
1215                 ]
1216             )
1217             # Configure IPSec SAD entries
1218             ckeys = [bytes()] * existing_tunnels
1219             ikeys = [bytes()] * existing_tunnels
1220             cmd = u"ipsec_sad_entry_add"
1221             c_key = dict(
1222                 length=0,
1223                 data=None
1224             )
1225             i_key = dict(
1226                 length=0,
1227                 data=None
1228             )
1229             sad_entry = dict(
1230                 sad_id=None,
1231                 spi=None,
1232                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1233                 crypto_algorithm=crypto_alg.alg_int_repr,
1234                 crypto_key=c_key,
1235                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1236                 integrity_key=i_key,
1237                 flags=None,
1238                 tunnel=dict(
1239                     src=0,
1240                     dst=0,
1241                     table_id=0,
1242                     encap_decap_flags=int(
1243                         TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1244                     ),
1245                     dscp=int(IpDscp.IP_API_DSCP_CS0),
1246                 ),
1247                 salt=0,
1248                 udp_src_port=IPSEC_UDP_PORT_NONE,
1249                 udp_dst_port=IPSEC_UDP_PORT_NONE,
1250             )
1251             args = dict(entry=sad_entry)
1252             for i in range(existing_tunnels, n_tunnels):
1253                 ckeys.append(
1254                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1255                 )
1256                 ikeys.append(
1257                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1258                 )
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
1262
1263                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1264                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1265                 if integ_alg:
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
1270                 )
1271                 papi_exec.add(
1272                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1273                 )
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
1277
1278                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1279                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1280                 if integ_alg:
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
1286                 )
1287                 papi_exec.add(
1288                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1289                 )
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"
1295             n_hop = dict(
1296                 address=0,
1297                 via_label=MPLS_LABEL_INVALID,
1298                 obj_id=Constants.BITWISE_NON_ZERO
1299             )
1300             ipsec_tunnel_protect = dict(
1301                 sw_if_index=None,
1302                 nh=n_hop,
1303                 sa_out=None,
1304                 n_sa_in=1,
1305                 sa_in=None
1306             )
1307             args = dict(
1308                 tunnel=ipsec_tunnel_protect
1309             )
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]
1314                 papi_exec.add(
1315                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1316                 )
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)
1320
1321             # Configure unnumbered interfaces
1322             cmd = u"sw_interface_set_unnumbered"
1323             args = dict(
1324                 is_add=True,
1325                 sw_if_index=InterfaceUtil.get_interface_index(
1326                     nodes[u"DUT1"], if1_key
1327                 ),
1328                 unnumbered_sw_if_index=0
1329             )
1330             for i in range(existing_tunnels, n_tunnels):
1331                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1332                 papi_exec.add(
1333                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1334                 )
1335             # Set interfaces up
1336             cmd = u"sw_interface_set_flags"
1337             args = dict(
1338                 sw_if_index=0,
1339                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1340             )
1341             for i in range(existing_tunnels, n_tunnels):
1342                 args[u"sw_if_index"] = ipip_tunnels[i]
1343                 papi_exec.add(
1344                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1345                 )
1346             # Configure IP routes
1347             cmd = u"ip_route_add_del"
1348             args = dict(
1349                 is_add=1,
1350                 is_multipath=0,
1351                 route=None
1352             )
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]
1358                 )
1359                 papi_exec.add(
1360                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1361                 )
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)
1365
1366         return ckeys, ikeys
1367
1368     @staticmethod
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.
1373
1374         This method accesses keys generated by DUT1 method
1375         and does not return anything.
1376
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.
1392         :type nodes: dict
1393         :type tun_ips: dict
1394         :type if2_key: str
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
1401         :type spi_d: dict
1402         :type existing_tunnels: int
1403         """
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"
1408                 args = dict(
1409                     sw_if_index=InterfaceUtil.get_interface_index(
1410                         nodes[u"DUT2"], if2_key
1411                     ),
1412                     is_add=True,
1413                     del_all=False,
1414                     prefix=IPUtil.create_prefix_object(
1415                         tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1416                         else 24
1417                     )
1418                 )
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"
1424             ipip_tunnel = dict(
1425                 instance=Constants.BITWISE_NON_ZERO,
1426                 src=None,
1427                 dst=None,
1428                 table_id=0,
1429                 flags=int(
1430                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1431                 ),
1432                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1433                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1434             )
1435             args = dict(
1436                 tunnel=ipip_tunnel
1437             )
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(
1441                     tun_ips[u"ip2"]
1442                 )
1443                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1444                     tun_ips[u"ip1"] + i * addr_incr
1445                 )
1446                 papi_exec.add(
1447                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1448                 )
1449             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1450                 f" {nodes[u'DUT2'][u'host']}"
1451             ipip_tunnels.extend(
1452                 [
1453                     reply[u"sw_if_index"]
1454                     for reply in papi_exec.get_replies(err_msg)
1455                     if u"sw_if_index" in reply
1456                 ]
1457             )
1458             # Configure IPSec SAD entries
1459             cmd = u"ipsec_sad_entry_add"
1460             c_key = dict(
1461                 length=0,
1462                 data=None
1463             )
1464             i_key = dict(
1465                 length=0,
1466                 data=None
1467             )
1468             sad_entry = dict(
1469                 sad_id=None,
1470                 spi=None,
1471                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1472                 crypto_algorithm=crypto_alg.alg_int_repr,
1473                 crypto_key=c_key,
1474                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1475                 integrity_key=i_key,
1476                 flags=None,
1477                 tunnel=dict(
1478                     src=0,
1479                     dst=0,
1480                     table_id=0,
1481                     encap_decap_flags=int(
1482                         TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1483                     ),
1484                     dscp=int(IpDscp.IP_API_DSCP_CS0),
1485                 ),
1486                 salt=0,
1487                 udp_src_port=IPSEC_UDP_PORT_NONE,
1488                 udp_dst_port=IPSEC_UDP_PORT_NONE,
1489             )
1490             args = dict(entry=sad_entry)
1491             for i in range(existing_tunnels, n_tunnels):
1492                 ckeys.append(
1493                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1494                 )
1495                 ikeys.append(
1496                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1497                 )
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
1501
1502                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1503                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1504                 if integ_alg:
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
1509                 )
1510                 papi_exec.add(
1511                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1512                 )
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
1516
1517                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1518                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1519                 if integ_alg:
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
1525                 )
1526                 papi_exec.add(
1527                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1528                 )
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"
1534             n_hop = dict(
1535                 address=0,
1536                 via_label=MPLS_LABEL_INVALID,
1537                 obj_id=Constants.BITWISE_NON_ZERO
1538             )
1539             ipsec_tunnel_protect = dict(
1540                 sw_if_index=None,
1541                 nh=n_hop,
1542                 sa_out=None,
1543                 n_sa_in=1,
1544                 sa_in=None
1545             )
1546             args = dict(
1547                 tunnel=ipsec_tunnel_protect
1548             )
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]
1553                 papi_exec.add(
1554                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1555                 )
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)
1559
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,
1566                     interface=if2_key,
1567                     gateway=(tun_ips[u"ip2"] - 1).compressed
1568                 )
1569                 args = dict(
1570                     is_add=1,
1571                     is_multipath=0,
1572                     route=route
1573                 )
1574                 papi_exec.add(cmd, **args)
1575             # Configure unnumbered interfaces
1576             cmd = u"sw_interface_set_unnumbered"
1577             args = dict(
1578                 is_add=True,
1579                 sw_if_index=InterfaceUtil.get_interface_index(
1580                     nodes[u"DUT2"], if2_key
1581                 ),
1582                 unnumbered_sw_if_index=0
1583             )
1584             for i in range(existing_tunnels, n_tunnels):
1585                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1586                 papi_exec.add(
1587                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1588                 )
1589             # Set interfaces up
1590             cmd = u"sw_interface_set_flags"
1591             args = dict(
1592                 sw_if_index=0,
1593                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1594             )
1595             for i in range(existing_tunnels, n_tunnels):
1596                 args[u"sw_if_index"] = ipip_tunnels[i]
1597                 papi_exec.add(
1598                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1599                 )
1600             # Configure IP routes
1601             cmd = u"ip_route_add_del"
1602             args = dict(
1603                 is_add=1,
1604                 is_multipath=0,
1605                 route=None
1606             )
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]
1612                 )
1613                 papi_exec.add(
1614                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1615                 )
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)
1619
1620     @staticmethod
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.
1626
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.
1630
1631         :param nodes: VPP nodes to create tunnel interfaces.
1632         :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1633             address.
1634         :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1635             address.
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.
1652         :type nodes: dict
1653         :type tun_if1_ip_addr: str
1654         :type tun_if2_ip_addr: str
1655         :type if1_key: str
1656         :type if2_key: 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]
1667         """
1668         n_tunnels = int(n_tunnels)
1669         existing_tunnels = int(existing_tunnels)
1670         spi_d = dict(
1671             spi_1=100000,
1672             spi_2=200000
1673         )
1674         tun_ips = dict(
1675             ip1=ip_address(tun_if1_ip_addr),
1676             ip2=ip_address(tun_if2_ip_addr)
1677         )
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)
1682
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
1686         )
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,
1691                 existing_tunnels
1692             )
1693
1694         if return_keys:
1695             return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1696         return None
1697
1698     @staticmethod
1699     def _create_ipsec_script_files(dut, instances):
1700         """Create script files for configuring IPsec in containers
1701
1702         :param dut: DUT node on which to create the script files
1703         :param instances: number of containers on DUT node
1704         :type dut: string
1705         :type instances: int
1706         """
1707         scripts = []
1708         for cnf in range(0, instances):
1709             script_filename = (
1710                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1711             )
1712             scripts.append(open(script_filename, 'w'))
1713         return scripts
1714
1715     @staticmethod
1716     def _close_and_copy_ipsec_script_files(
1717             dut, nodes, instances, scripts):
1718         """Close created scripts and copy them to containers
1719
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
1724         :type dut: string
1725         :type nodes: dict
1726         :type instances: int
1727         :type scripts: dict
1728         """
1729         for cnf in range(0, instances):
1730             scripts[cnf].close()
1731             script_filename = (
1732                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1733             )
1734             scp_node(nodes[dut], script_filename, script_filename)
1735
1736
1737     @staticmethod
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.
1742
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.
1756         :type nodes: dict
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
1766         """
1767         spi_1 = 100000
1768         spi_2 = 200000
1769         addr_incr = 1 << (32 - raddr_range)
1770
1771         dut1_scripts = IPsecUtil._create_ipsec_script_files(
1772             u"DUT1", n_instances
1773         )
1774         dut2_scripts = IPsecUtil._create_ipsec_script_files(
1775             u"DUT2", n_instances
1776         )
1777
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"
1782             )
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"
1786             )
1787
1788         for tnl in range(0, n_tunnels):
1789             cnf = tnl % n_instances
1790             ckey = getattr(
1791                 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1792             )
1793             integ = u""
1794             ikey = getattr(
1795                 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1796             )
1797             if integ_alg:
1798                 integ = (
1799                     f"integ-alg {integ_alg.alg_name} "
1800                     f"local-integ-key {ikey} "
1801                     f"remote-integ-key {ikey} "
1802                 )
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} "
1816                 f"salt 0x0 "
1817                 f"{integ} \n"
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"
1822             )
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} "
1836                 f"salt 0x0 "
1837                 f"{integ}\n"
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"
1843             )
1844
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)
1849
1850     @staticmethod
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.
1856
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
1872             incremental step.
1873         :type nodes: dict
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
1885         """
1886         spd_id = 1
1887         p_hi = 100
1888         p_lo = 10
1889         sa_id_1 = 100000
1890         sa_id_2 = 200000
1891         spi_1 = 300000
1892         spi_2 = 400000
1893
1894         crypto_key = gen_key(
1895             IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1896         ).decode()
1897         integ_key = gen_key(
1898             IPsecUtil.get_integ_alg_key_len(integ_alg)
1899         ).decode() if integ_alg else u""
1900
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)
1907
1908         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1909         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1910
1911         addr_incr = 1 << (128 - 96) if ip_address(tunnel_ip1).version == 6 \
1912             else 1 << (32 - 24)
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
1920
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
1925             )
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
1930             )
1931
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
1935         )
1936
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))
1942         )
1943
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
1947         )
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))
1953         )
1954
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)
1960
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
1970
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
1975                 )
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
1980                 )
1981
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,
1985                 tunnel_addr_incr
1986             )
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))
1993             )
1994
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,
1998                 tunnel_addr_incr
1999             )
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))
2006             )
2007
2008     @staticmethod
2009     def vpp_ipsec_show_all(node):
2010         """Run "show ipsec all" debug CLI command.
2011
2012         :param node: Node to run command on.
2013         :type node: dict
2014         """
2015         PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2016
2017     @staticmethod
2018     def show_ipsec_security_association(node):
2019         """Show IPSec security association.
2020
2021         :param node: DUT node.
2022         :type node: dict
2023         """
2024         cmds = [
2025             u"ipsec_sa_v3_dump"
2026         ]
2027         PapiSocketExecutor.dump_and_log(node, cmds)
2028
2029     @staticmethod
2030     def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2031         """Ipsec flow enable rss action.
2032
2033         :param node: DUT node.
2034         :param proto: The flow protocol.
2035         :param type: RSS type.
2036         :param function: RSS function.
2037
2038         :type node: dict
2039         :type proto: str
2040         :type type: str
2041         :type function: str
2042         :returns: flow_index.
2043         """
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]
2049
2050         return flow_index
2051
2052     @staticmethod
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.
2056
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.
2062
2063         :type node: dict
2064         :type n_flows: int
2065         :type rx_queues: int
2066         :type spi_start: int
2067         :type interface: str
2068         :returns: flow_index.
2069         """
2070
2071         for i in range(0, n_flows):
2072             rx_queue = i%rx_queues
2073
2074             spi = spi_start + i
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)