Add 100k tunnels ipsec policy mode with fastpath enabled test suite
[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 resources.libraries.python.Constants import Constants
26 from resources.libraries.python.IncrementUtil import ObjIncrement
27 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
28     InterfaceStatusFlags
29 from resources.libraries.python.IPAddress import IPAddress
30 from resources.libraries.python.IPUtil import IPUtil, IpDscp, \
31     MPLS_LABEL_INVALID, NetworkIncrement
32 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
33 from resources.libraries.python.ssh import scp_node
34 from resources.libraries.python.topology import Topology, NodeType
35 from resources.libraries.python.VPPUtil import VPPUtil
36 from resources.libraries.python.FlowUtil import FlowUtil
37
38
39 IPSEC_UDP_PORT_NONE = 0xffff
40
41
42 def gen_key(length):
43     """Generate random string as a key.
44
45     :param length: Length of generated payload.
46     :type length: int
47     :returns: The generated payload.
48     :rtype: bytes
49     """
50     return u"".join(
51         choice(ascii_letters) for _ in range(length)
52     ).encode(encoding=u"utf-8")
53
54
55 class PolicyAction(Enum):
56     """Policy actions."""
57     BYPASS = (u"bypass", 0)
58     DISCARD = (u"discard", 1)
59     PROTECT = (u"protect", 3)
60
61     def __init__(self, policy_name, policy_int_repr):
62         self.policy_name = policy_name
63         self.policy_int_repr = policy_int_repr
64
65     def __str__(self):
66         return self.policy_name
67
68     def __int__(self):
69         return self.policy_int_repr
70
71
72 class CryptoAlg(Enum):
73     """Encryption algorithms."""
74     AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
75     AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
76     AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
77     AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
78
79     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
80         self.alg_name = alg_name
81         self.alg_int_repr = alg_int_repr
82         self.scapy_name = scapy_name
83         self.key_len = key_len
84
85
86 class IntegAlg(Enum):
87     """Integrity algorithm."""
88     SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
89     SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
90
91     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
92         self.alg_name = alg_name
93         self.alg_int_repr = alg_int_repr
94         self.scapy_name = scapy_name
95         self.key_len = key_len
96
97
98 class IPsecProto(IntEnum):
99     """IPsec protocol."""
100     IPSEC_API_PROTO_ESP = 50
101     IPSEC_API_PROTO_AH = 51
102
103
104 class IPsecSadFlags(IntEnum):
105     """IPsec Security Association Database flags."""
106     IPSEC_API_SAD_FLAG_NONE = 0
107     # Enable extended sequence numbers
108     IPSEC_API_SAD_FLAG_USE_ESN = 0x01
109     # Enable Anti - replay
110     IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02
111     # IPsec tunnel mode if non-zero, else transport mode
112     IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04
113     # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
114     # only valid if is_tunnel is non-zero
115     IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08
116     # Enable UDP encapsulation for NAT traversal
117     IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10
118     # IPsec SA is or inbound traffic
119     IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
120
121
122 class TunnelEncpaDecapFlags(IntEnum):
123     """Flags controlling tunnel behaviour."""
124     TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
125     # at encap, copy the DF bit of the payload into the tunnel header
126     TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
127     # at encap, set the DF bit in the tunnel header
128     TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
129     # at encap, copy the DSCP bits of the payload into the tunnel header
130     TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
131     # at encap, copy the ECN bit of the payload into the tunnel header
132     TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
133     # at decap, copy the ECN bit of the tunnel header into the payload
134     TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
135
136
137 class TunnelMode(IntEnum):
138     """Tunnel modes."""
139     # point-to-point
140     TUNNEL_API_MODE_P2P = 0
141     # multi-point
142     TUNNEL_API_MODE_MP = 1
143
144
145 class IPsecUtil:
146     """IPsec utilities."""
147
148     @staticmethod
149     def policy_action_bypass():
150         """Return policy action bypass.
151
152         :returns: PolicyAction enum BYPASS object.
153         :rtype: PolicyAction
154         """
155         return PolicyAction.BYPASS
156
157     @staticmethod
158     def policy_action_discard():
159         """Return policy action discard.
160
161         :returns: PolicyAction enum DISCARD object.
162         :rtype: PolicyAction
163         """
164         return PolicyAction.DISCARD
165
166     @staticmethod
167     def policy_action_protect():
168         """Return policy action protect.
169
170         :returns: PolicyAction enum PROTECT object.
171         :rtype: PolicyAction
172         """
173         return PolicyAction.PROTECT
174
175     @staticmethod
176     def crypto_alg_aes_cbc_128():
177         """Return encryption algorithm aes-cbc-128.
178
179         :returns: CryptoAlg enum AES_CBC_128 object.
180         :rtype: CryptoAlg
181         """
182         return CryptoAlg.AES_CBC_128
183
184     @staticmethod
185     def crypto_alg_aes_cbc_256():
186         """Return encryption algorithm aes-cbc-256.
187
188         :returns: CryptoAlg enum AES_CBC_256 object.
189         :rtype: CryptoAlg
190         """
191         return CryptoAlg.AES_CBC_256
192
193     @staticmethod
194     def crypto_alg_aes_gcm_128():
195         """Return encryption algorithm aes-gcm-128.
196
197         :returns: CryptoAlg enum AES_GCM_128 object.
198         :rtype: CryptoAlg
199         """
200         return CryptoAlg.AES_GCM_128
201
202     @staticmethod
203     def crypto_alg_aes_gcm_256():
204         """Return encryption algorithm aes-gcm-256.
205
206         :returns: CryptoAlg enum AES_GCM_128 object.
207         :rtype: CryptoAlg
208         """
209         return CryptoAlg.AES_GCM_256
210
211     @staticmethod
212     def get_crypto_alg_key_len(crypto_alg):
213         """Return encryption algorithm key length.
214
215         :param crypto_alg: Encryption algorithm.
216         :type crypto_alg: CryptoAlg
217         :returns: Key length.
218         :rtype: int
219         """
220         return crypto_alg.key_len
221
222     @staticmethod
223     def get_crypto_alg_scapy_name(crypto_alg):
224         """Return encryption algorithm scapy name.
225
226         :param crypto_alg: Encryption algorithm.
227         :type crypto_alg: CryptoAlg
228         :returns: Algorithm scapy name.
229         :rtype: str
230         """
231         return crypto_alg.scapy_name
232
233     @staticmethod
234     def integ_alg_sha_256_128():
235         """Return integrity algorithm SHA-256-128.
236
237         :returns: IntegAlg enum SHA_256_128 object.
238         :rtype: IntegAlg
239         """
240         return IntegAlg.SHA_256_128
241
242     @staticmethod
243     def integ_alg_sha_512_256():
244         """Return integrity algorithm SHA-512-256.
245
246         :returns: IntegAlg enum SHA_512_256 object.
247         :rtype: IntegAlg
248         """
249         return IntegAlg.SHA_512_256
250
251     @staticmethod
252     def get_integ_alg_key_len(integ_alg):
253         """Return integrity algorithm key length.
254
255         None argument is accepted, returning zero.
256
257         :param integ_alg: Integrity algorithm.
258         :type integ_alg: Optional[IntegAlg]
259         :returns: Key length.
260         :rtype: int
261         """
262         return 0 if integ_alg is None else integ_alg.key_len
263
264     @staticmethod
265     def get_integ_alg_scapy_name(integ_alg):
266         """Return integrity algorithm scapy name.
267
268         :param integ_alg: Integrity algorithm.
269         :type integ_alg: IntegAlg
270         :returns: Algorithm scapy name.
271         :rtype: str
272         """
273         return integ_alg.scapy_name
274
275     @staticmethod
276     def ipsec_proto_esp():
277         """Return IPSec protocol ESP.
278
279         :returns: IPsecProto enum ESP object.
280         :rtype: IPsecProto
281         """
282         return int(IPsecProto.IPSEC_API_PROTO_ESP)
283
284     @staticmethod
285     def ipsec_proto_ah():
286         """Return IPSec protocol AH.
287
288         :returns: IPsecProto enum AH object.
289         :rtype: IPsecProto
290         """
291         return int(IPsecProto.IPSEC_API_PROTO_AH)
292
293     @staticmethod
294     def vpp_ipsec_select_backend(node, protocol, index=1):
295         """Select IPsec backend.
296
297         :param node: VPP node to select IPsec backend on.
298         :param protocol: IPsec protocol.
299         :param index: Backend index.
300         :type node: dict
301         :type protocol: IPsecProto
302         :type index: int
303         :raises RuntimeError: If failed to select IPsec backend or if no API
304             reply received.
305         """
306         cmd = u"ipsec_select_backend"
307         err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
308         args = dict(
309             protocol=protocol,
310             index=index
311         )
312         with PapiSocketExecutor(node) as papi_exec:
313             papi_exec.add(cmd, **args).get_reply(err_msg)
314
315     @staticmethod
316     def vpp_ipsec_set_async_mode(node, async_enable=1):
317         """Set IPsec async mode on|off.
318
319         :param node: VPP node to set IPsec async mode.
320         :param async_enable: Async mode on or off.
321         :type node: dict
322         :type async_enable: int
323         :raises RuntimeError: If failed to set IPsec async mode or if no API
324             reply received.
325         """
326         cmd = u"ipsec_set_async_mode"
327         err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
328         args = dict(
329             async_enable=async_enable
330         )
331         with PapiSocketExecutor(node) as papi_exec:
332             papi_exec.add(cmd, **args).get_reply(err_msg)
333
334     @staticmethod
335     def vpp_ipsec_crypto_sw_scheduler_set_worker(
336             node, workers, crypto_enable=False):
337         """Enable or disable crypto on specific vpp worker threads.
338
339         :param node: VPP node to enable or disable crypto for worker threads.
340         :param workers: List of VPP thread numbers.
341         :param crypto_enable: Disable or enable crypto work.
342         :type node: dict
343         :type workers: Iterable[int]
344         :type crypto_enable: bool
345         :raises RuntimeError: If failed to enable or disable crypto for worker
346             thread or if no API reply received.
347         """
348         for worker in workers:
349             cmd = u"crypto_sw_scheduler_set_worker"
350             err_msg = f"Failed to disable/enable crypto for worker thread " \
351                 f"on host {node[u'host']}"
352             args = dict(
353                 worker_index=worker - 1,
354                 crypto_enable=crypto_enable
355             )
356             with PapiSocketExecutor(node) as papi_exec:
357                 papi_exec.add(cmd, **args).get_reply(err_msg)
358
359     @staticmethod
360     def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
361             nodes, workers, crypto_enable=False):
362         """Enable or disable crypto on specific vpp worker threads.
363
364         :param node: VPP node to enable or disable crypto for worker threads.
365         :param workers: List of VPP thread numbers.
366         :param crypto_enable: Disable or enable crypto work.
367         :type node: dict
368         :type workers: Iterable[int]
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 in nodes.values():
374             if node[u"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                 for item in thread_data:
381                     if str(item.cpu_id) in workers.split(u","):
382                         worker_ids.append(item.id)
383
384                 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
385                     node, workers=worker_ids, crypto_enable=crypto_enable
386                 )
387
388     @staticmethod
389     def vpp_ipsec_add_sad_entry(
390             node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
391             integ_key=u"", tunnel_src=None, tunnel_dst=None):
392         """Create Security Association Database entry on the VPP node.
393
394         :param node: VPP node to add SAD entry on.
395         :param sad_id: SAD entry ID.
396         :param spi: Security Parameter Index of this SAD entry.
397         :param crypto_alg: The encryption algorithm name.
398         :param crypto_key: The encryption key string.
399         :param integ_alg: The integrity algorithm name.
400         :param integ_key: The integrity key string.
401         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
402             specified ESP transport mode is used.
403         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
404             not specified ESP transport mode is used.
405         :type node: dict
406         :type sad_id: int
407         :type spi: int
408         :type crypto_alg: CryptoAlg
409         :type crypto_key: str
410         :type integ_alg: Optional[IntegAlg]
411         :type integ_key: str
412         :type tunnel_src: str
413         :type tunnel_dst: str
414         """
415         if isinstance(crypto_key, str):
416             crypto_key = crypto_key.encode(encoding=u"utf-8")
417         if isinstance(integ_key, str):
418             integ_key = integ_key.encode(encoding=u"utf-8")
419         ckey = dict(
420             length=len(crypto_key),
421             data=crypto_key
422         )
423         ikey = dict(
424             length=len(integ_key),
425             data=integ_key if integ_key else 0
426         )
427
428         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
429         if tunnel_src and tunnel_dst:
430             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
431             src_addr = ip_address(tunnel_src)
432             dst_addr = ip_address(tunnel_dst)
433             if src_addr.version == 6:
434                 flags = \
435                     flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
436         else:
437             src_addr = u""
438             dst_addr = u""
439
440         cmd = u"ipsec_sad_entry_add"
441         err_msg = f"Failed to add Security Association Database entry " \
442             f"on host {node[u'host']}"
443         sad_entry = dict(
444             sad_id=int(sad_id),
445             spi=int(spi),
446             crypto_algorithm=crypto_alg.alg_int_repr,
447             crypto_key=ckey,
448             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
449             integrity_key=ikey,
450             flags=flags,
451             tunnel=dict(
452                 src=str(src_addr),
453                 dst=str(dst_addr),
454                 table_id=0,
455                 encap_decap_flags=int(
456                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
457                 ),
458                 dscp=int(IpDscp.IP_API_DSCP_CS0),
459             ),
460             protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
461             udp_src_port=4500,  # default value in api
462             udp_dst_port=4500  # default value in api
463         )
464         args = dict(entry=sad_entry)
465         with PapiSocketExecutor(node) as papi_exec:
466             papi_exec.add(cmd, **args).get_reply(err_msg)
467
468     @staticmethod
469     def vpp_ipsec_add_sad_entries(
470             node, n_entries, sad_id, spi, crypto_alg, crypto_key,
471             integ_alg=None, integ_key=u"", tunnel_src=None,tunnel_dst=None,
472             tunnel_addr_incr=True):
473         """Create multiple Security Association Database entries on VPP node.
474
475         :param node: VPP node to add SAD entry on.
476         :param n_entries: Number of SAD entries to be created.
477         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
478             id incremented by 1.
479         :param spi: Security Parameter Index of first SAD entry. All subsequent
480             SAD entries will have spi incremented by 1.
481         :param crypto_alg: The encryption algorithm name.
482         :param crypto_key: The encryption key string.
483         :param integ_alg: The integrity algorithm name.
484         :param integ_key: The integrity key string.
485         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
486             specified ESP transport mode is used.
487         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
488             not specified ESP transport mode is used.
489         :param tunnel_addr_incr: Enable or disable tunnel IP address
490             incremental step.
491         :type node: dict
492         :type n_entries: int
493         :type sad_id: int
494         :type spi: int
495         :type crypto_alg: CryptoAlg
496         :type crypto_key: str
497         :type integ_alg: Optional[IntegAlg]
498         :type integ_key: str
499         :type tunnel_src: str
500         :type tunnel_dst: str
501         :type tunnel_addr_incr: bool
502         """
503         if isinstance(crypto_key, str):
504             crypto_key = crypto_key.encode(encoding=u"utf-8")
505         if isinstance(integ_key, str):
506             integ_key = integ_key.encode(encoding=u"utf-8")
507         if tunnel_src and tunnel_dst:
508             src_addr = ip_address(tunnel_src)
509             dst_addr = ip_address(tunnel_dst)
510         else:
511             src_addr = u""
512             dst_addr = u""
513
514         if tunnel_addr_incr:
515             addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
516                 else 1 << (32 - 24)
517         else:
518             addr_incr = 0
519
520         ckey = dict(
521             length=len(crypto_key),
522             data=crypto_key
523         )
524         ikey = dict(
525             length=len(integ_key),
526             data=integ_key if integ_key else 0
527         )
528
529         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
530         if tunnel_src and tunnel_dst:
531             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
532             if src_addr.version == 6:
533                 flags = flags | int(
534                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
535                 )
536
537         cmd = u"ipsec_sad_entry_add"
538         err_msg = f"Failed to add Security Association Database entry " \
539             f"on host {node[u'host']}"
540
541         sad_entry = dict(
542             sad_id=int(sad_id),
543             spi=int(spi),
544             crypto_algorithm=crypto_alg.alg_int_repr,
545             crypto_key=ckey,
546             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
547             integrity_key=ikey,
548             flags=flags,
549             tunnel=dict(
550                 src=str(src_addr),
551                 dst=str(dst_addr),
552                 table_id=0,
553                 encap_decap_flags=int(
554                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
555                 ),
556                 dscp=int(IpDscp.IP_API_DSCP_CS0),
557             ),
558             protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
559             udp_src_port=4500,  # default value in api
560             udp_dst_port=4500,  # default value in api
561         )
562         args = dict(entry=sad_entry)
563         with PapiSocketExecutor(node, is_async=True) as papi_exec:
564             for i in range(n_entries):
565                 args[u"entry"][u"sad_id"] = int(sad_id) + i
566                 args[u"entry"][u"spi"] = int(spi) + i
567                 args[u"entry"][u"tunnel"][u"src"] = (
568                     str(src_addr + i * addr_incr)
569                     if tunnel_src and tunnel_dst else src_addr
570                 )
571                 args[u"entry"][u"tunnel"][u"dst"] = (
572                     str(dst_addr + i * addr_incr)
573                     if tunnel_src and tunnel_dst else dst_addr
574                 )
575                 history = bool(not 1 < i < n_entries - 2)
576                 papi_exec.add(cmd, history=history, **args)
577             papi_exec.get_replies(err_msg)
578
579     @staticmethod
580     def vpp_ipsec_set_ip_route(
581             node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
582             raddr_range, dst_mac=None):
583         """Set IP address and route on interface.
584
585         :param node: VPP node to add config on.
586         :param n_tunnels: Number of tunnels to create.
587         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
588         :param traffic_addr: Traffic destination IP address to route.
589         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
590         :param interface: Interface key on node 1.
591         :param raddr_range: Mask specifying range of Policy selector Remote IP
592             addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
593             in case of IPv6.
594         :param dst_mac: The MAC address of destination tunnels.
595         :type node: dict
596         :type n_tunnels: int
597         :type tunnel_src: str
598         :type traffic_addr: str
599         :type tunnel_dst: str
600         :type interface: str
601         :type raddr_range: int
602         :type dst_mac: str
603         """
604         tunnel_src = ip_address(tunnel_src)
605         tunnel_dst = ip_address(tunnel_dst)
606         traffic_addr = ip_address(traffic_addr)
607         tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
608         addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
609             else 1 << (32 - raddr_range)
610
611         cmd1 = u"sw_interface_add_del_address"
612         args1 = dict(
613             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
614             is_add=True,
615             del_all=False,
616             prefix=None
617         )
618         cmd2 = u"ip_route_add_del"
619         args2 = dict(
620             is_add=1,
621             is_multipath=0,
622             route=None
623         )
624         cmd3 = u"ip_neighbor_add_del"
625         args3 = dict(
626             is_add=True,
627             neighbor=dict(
628                 sw_if_index=Topology.get_interface_sw_index(node, interface),
629                 flags=0,
630                 mac_address=str(dst_mac),
631                 ip_address=None
632             )
633         )
634         err_msg = f"Failed to configure IP addresses, IP routes and " \
635             f"IP neighbor on interface {interface} on host {node[u'host']}" \
636             if dst_mac \
637             else f"Failed to configure IP addresses and IP routes " \
638                  f"on interface {interface} on host {node[u'host']}"
639
640         with PapiSocketExecutor(node, is_async=True) as papi_exec:
641             for i in range(n_tunnels):
642                 tunnel_dst_addr = tunnel_dst + i * addr_incr
643                 args1[u"prefix"] = IPUtil.create_prefix_object(
644                     tunnel_src + i * addr_incr, raddr_range
645                 )
646                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
647                     node, traffic_addr + i,
648                     prefix_len=tunnel_dst_prefix,
649                     interface=interface, gateway=tunnel_dst_addr
650                 )
651                 history = bool(not 1 < i < n_tunnels - 2)
652                 papi_exec.add(cmd1, history=history, **args1)
653                 papi_exec.add(cmd2, history=history, **args2)
654
655                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
656                     node, tunnel_dst_addr,
657                     prefix_len=tunnel_dst_prefix,
658                     interface=interface, gateway=tunnel_dst_addr
659                 )
660                 papi_exec.add(cmd2, history=history, **args2)
661
662                 if dst_mac:
663                     args3[u"neighbor"][u"ip_address"] = ip_address(
664                         tunnel_dst_addr
665                     )
666                     papi_exec.add(cmd3, history=history, **args3)
667             papi_exec.get_replies(err_msg)
668
669     @staticmethod
670     def vpp_ipsec_add_spd(node, spd_id):
671         """Create Security Policy Database on the VPP node.
672
673         :param node: VPP node to add SPD on.
674         :param spd_id: SPD ID.
675         :type node: dict
676         :type spd_id: int
677         """
678         cmd = u"ipsec_spd_add_del"
679         err_msg = f"Failed to add Security Policy Database " \
680             f"on host {node[u'host']}"
681         args = dict(
682             is_add=True,
683             spd_id=int(spd_id)
684         )
685         with PapiSocketExecutor(node) as papi_exec:
686             papi_exec.add(cmd, **args).get_reply(err_msg)
687
688     @staticmethod
689     def vpp_ipsec_spd_add_if(node, spd_id, interface):
690         """Add interface to the Security Policy Database.
691
692         :param node: VPP node.
693         :param spd_id: SPD ID to add interface on.
694         :param interface: Interface name or sw_if_index.
695         :type node: dict
696         :type spd_id: int
697         :type interface: str or int
698         """
699         cmd = u"ipsec_interface_add_del_spd"
700         err_msg = f"Failed to add interface {interface} to Security Policy " \
701             f"Database {spd_id} on host {node[u'host']}"
702         args = dict(
703             is_add=True,
704             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
705             spd_id=int(spd_id)
706         )
707         with PapiSocketExecutor(node) as papi_exec:
708             papi_exec.add(cmd, **args).get_reply(err_msg)
709
710     @staticmethod
711     def vpp_ipsec_create_spds_match_nth_entry(
712             node, dir1_interface, dir2_interface, entry_amount,
713             local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
714             inbound=False, bidirectional=True):
715         """Create one matching SPD entry for inbound or outbound traffic on
716         a DUT for each traffic direction and also create entry_amount - 1
717         non-matching SPD entries. Create a Security Policy Database on each
718         outbound interface where these entries will be configured.
719         The matching SPD entry will have the lowest priority, input action and
720         will be configured to match the IP flow. The non-matching entries will
721         be the same, except with higher priority and non-matching IP flows.
722
723         Action Protect is currently not supported.
724
725         :param node: VPP node to configured the SPDs and their entries.
726         :param dir1_interface: The interface in direction 1 where the entries
727             will be checked.
728         :param dir2_interface: The interface in direction 2 where the entries
729             will be checked.
730         :param entry_amount: The number of SPD entries to configure. If
731             entry_amount == 1, no non-matching entries will be configured.
732         :param local_addr_range: Matching local address range in direction 1
733             in format IP/prefix or IP/mask. If no mask is provided, it's
734             considered to be /32.
735         :param remote_addr_range: Matching remote address range in
736             direction 1 in format IP/prefix or IP/mask. If no mask is
737             provided, it's considered to be /32.
738         :param action: Policy action.
739         :param inbound: If True policy is for inbound traffic, otherwise
740             outbound.
741         :param bidirectional: When True, will create SPDs in both directions
742             of traffic. When False, only in one direction.
743         :type node: dict
744         :type dir1_interface: Union[string, int]
745         :type dir2_interface: Union[string, int]
746         :type entry_amount: int
747         :type local_addr_range:
748             Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
749         :type remote_addr_range:
750             Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
751         :type action: IPsecUtil.PolicyAction
752         :type inbound: bool
753         :type bidirectional: bool
754         :raises NotImplementedError: When the action is PolicyAction.PROTECT.
755         """
756
757         if action == PolicyAction.PROTECT:
758             raise NotImplementedError('Policy action PROTECT is not supported.')
759
760         spd_id_dir1 = 1
761         spd_id_dir2 = 2
762         matching_priority = 1
763
764         IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
765         IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
766         # matching entry direction 1
767         IPsecUtil.vpp_ipsec_add_spd_entry(
768             node, spd_id_dir1, matching_priority, action,
769             inbound=inbound, laddr_range=local_addr_range,
770             raddr_range=remote_addr_range
771         )
772
773         if bidirectional:
774             IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
775             IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
776
777             # matching entry direction 2, the address ranges are switched
778             IPsecUtil.vpp_ipsec_add_spd_entry(
779                 node, spd_id_dir2, matching_priority, action,
780                 inbound=inbound, laddr_range=remote_addr_range,
781                 raddr_range=local_addr_range
782             )
783
784         # non-matching entries
785         no_match_entry_amount = entry_amount - 1
786         if no_match_entry_amount > 0:
787             # create a NetworkIncrement representation of the network,
788             # then skip the matching network
789             no_match_local_addr_range = NetworkIncrement(
790                 ip_network(local_addr_range)
791             )
792             next(no_match_local_addr_range)
793
794             no_match_remote_addr_range = NetworkIncrement(
795                 ip_network(remote_addr_range)
796             )
797             next(no_match_remote_addr_range)
798
799             # non-matching entries direction 1
800             IPsecUtil.vpp_ipsec_add_spd_entries(
801                 node, no_match_entry_amount, spd_id_dir1,
802                 ObjIncrement(matching_priority + 1, 1), action,
803                 inbound=inbound, laddr_range=no_match_local_addr_range,
804                 raddr_range=no_match_remote_addr_range
805             )
806
807             if bidirectional:
808                 # reset the networks so that we're using a unified config
809                 # the address ranges are switched
810                 no_match_remote_addr_range = NetworkIncrement(
811                     ip_network(local_addr_range)
812                 )
813                 next(no_match_remote_addr_range)
814
815                 no_match_local_addr_range = NetworkIncrement(
816                     ip_network(remote_addr_range)
817                 )
818                 next(no_match_local_addr_range)
819                 # non-matching entries direction 2
820                 IPsecUtil.vpp_ipsec_add_spd_entries(
821                     node, no_match_entry_amount, spd_id_dir2,
822                     ObjIncrement(matching_priority + 1, 1), action,
823                     inbound=inbound, laddr_range=no_match_local_addr_range,
824                     raddr_range=no_match_remote_addr_range
825                 )
826
827         IPsecUtil.vpp_ipsec_show_all(node)
828
829     @staticmethod
830     def _vpp_ipsec_add_spd_entry_internal(
831             executor, spd_id, priority, action, inbound=True, sa_id=None,
832             proto=None, laddr_range=None, raddr_range=None, lport_range=None,
833             rport_range=None, is_ipv6=False):
834         """Prepare to create Security Policy Database entry on the VPP node.
835
836         This just adds one more command to the executor.
837         The call site shall get replies once all entries are added,
838         to get speed benefit from async PAPI.
839
840         :param executor: Open PAPI executor (async handling) to add commands to.
841         :param spd_id: SPD ID to add entry on.
842         :param priority: SPD entry priority, higher number = higher priority.
843         :param action: Policy action.
844         :param inbound: If True policy is for inbound traffic, otherwise
845             outbound.
846         :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
847         :param proto: Policy selector next layer protocol number.
848         :param laddr_range: Policy selector local IPv4 or IPv6 address range
849             in format IP/prefix or IP/mask. If no mask is provided,
850             it's considered to be /32.
851         :param raddr_range: Policy selector remote 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 lport_range: Policy selector local TCP/UDP port range in format
855             <port_start>-<port_end>.
856         :param rport_range: Policy selector remote TCP/UDP port range in format
857             <port_start>-<port_end>.
858         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
859             not defined so it will default to address ::/0, otherwise False.
860         :type executor: PapiSocketExecutor
861         :type spd_id: int
862         :type priority: int
863         :type action: IPsecUtil.PolicyAction
864         :type inbound: bool
865         :type sa_id: int
866         :type proto: int
867         :type laddr_range: string
868         :type raddr_range: string
869         :type lport_range: string
870         :type rport_range: string
871         :type is_ipv6: bool
872         """
873         if laddr_range is None:
874             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
875
876         if raddr_range is None:
877             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
878
879         local_net = ip_network(laddr_range, strict=False)
880         remote_net = ip_network(raddr_range, strict=False)
881
882         cmd = u"ipsec_spd_entry_add_del_v2"
883
884         spd_entry = dict(
885             spd_id=int(spd_id),
886             priority=int(priority),
887             is_outbound=not inbound,
888             sa_id=int(sa_id) if sa_id else 0,
889             policy=int(action),
890             protocol=255 if proto is None else int(proto),
891             remote_address_start=IPAddress.create_ip_address_object(
892                 remote_net.network_address
893             ),
894             remote_address_stop=IPAddress.create_ip_address_object(
895                 remote_net.broadcast_address
896             ),
897             local_address_start=IPAddress.create_ip_address_object(
898                 local_net.network_address
899             ),
900             local_address_stop=IPAddress.create_ip_address_object(
901                 local_net.broadcast_address
902             ),
903             remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
904             else 0,
905             remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
906             else 65535,
907             local_port_start=int(lport_range.split(u"-")[0]) if lport_range
908             else 0,
909             local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
910             else 65535
911         )
912         args = dict(
913             is_add=True,
914             entry=spd_entry
915         )
916         executor.add(cmd, **args)
917
918     @staticmethod
919     def vpp_ipsec_add_spd_entry(
920             node, spd_id, priority, action, inbound=True, sa_id=None,
921             proto=None, laddr_range=None, raddr_range=None, lport_range=None,
922             rport_range=None, is_ipv6=False):
923         """Create Security Policy Database entry on the VPP node.
924
925         :param node: VPP node to add SPD entry on.
926         :param spd_id: SPD ID to add entry on.
927         :param priority: SPD entry priority, higher number = higher priority.
928         :param action: Policy action.
929         :param inbound: If True policy is for inbound traffic, otherwise
930             outbound.
931         :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
932         :param proto: Policy selector next layer protocol number.
933         :param laddr_range: Policy selector local IPv4 or IPv6 address range
934             in format IP/prefix or IP/mask. If no mask is provided,
935             it's considered to be /32.
936         :param raddr_range: Policy selector remote 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 lport_range: Policy selector local TCP/UDP port range in format
940             <port_start>-<port_end>.
941         :param rport_range: Policy selector remote TCP/UDP port range in format
942             <port_start>-<port_end>.
943         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
944             not defined so it will default to address ::/0, otherwise False.
945         :type node: dict
946         :type spd_id: int
947         :type priority: int
948         :type action: IPsecUtil.PolicyAction
949         :type inbound: bool
950         :type sa_id: int
951         :type proto: int
952         :type laddr_range: string
953         :type raddr_range: string
954         :type lport_range: string
955         :type rport_range: string
956         :type is_ipv6: bool
957         """
958         err_msg = f"Failed to add entry to Security Policy Database " \
959                   f"{spd_id} on host {node[u'host']}"
960         with PapiSocketExecutor(node, is_async=True) as papi_exec:
961             IPsecUtil._vpp_ipsec_add_spd_entry_internal(
962                 papi_exec, spd_id, priority, action, inbound, sa_id, proto,
963                 laddr_range, raddr_range, lport_range, rport_range, is_ipv6
964             )
965             papi_exec.get_replies(err_msg)
966
967     @staticmethod
968     def vpp_ipsec_add_spd_entries(
969             node, n_entries, spd_id, priority, action, inbound, sa_id=None,
970             proto=None, laddr_range=None, raddr_range=None, lport_range=None,
971             rport_range=None, is_ipv6=False):
972         """Create multiple Security Policy Database entries on the VPP node.
973
974         :param node: VPP node to add SPD entries on.
975         :param n_entries: Number of SPD entries to be added.
976         :param spd_id: SPD ID to add entries on.
977         :param priority: SPD entries priority, higher number = higher priority.
978         :param action: Policy action.
979         :param inbound: If True policy is for inbound traffic, otherwise
980             outbound.
981         :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
982         :param proto: Policy selector next layer protocol number.
983         :param laddr_range: Policy selector local IPv4 or IPv6 address range
984             in format IP/prefix or IP/mask. If no mask is provided,
985             it's considered to be /32.
986         :param raddr_range: Policy selector remote 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 lport_range: Policy selector local TCP/UDP port range in format
990             <port_start>-<port_end>.
991         :param rport_range: Policy selector remote TCP/UDP port range in format
992             <port_start>-<port_end>.
993         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
994             not defined so it will default to address ::/0, otherwise False.
995         :type node: dict
996         :type n_entries: int
997         :type spd_id: int
998         :type priority: IPsecUtil.ObjIncrement
999         :type action: IPsecUtil.PolicyAction
1000         :type inbound: bool
1001         :type sa_id: IPsecUtil.ObjIncrement
1002         :type proto: int
1003         :type laddr_range: IPsecUtil.NetworkIncrement
1004         :type raddr_range: IPsecUtil.NetworkIncrement
1005         :type lport_range: string
1006         :type rport_range: string
1007         :type is_ipv6: bool
1008         """
1009         if laddr_range is None:
1010             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1011             laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1012
1013         if raddr_range is None:
1014             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1015             raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1016
1017         lport_range_start = 0
1018         lport_range_stop = 65535
1019         if lport_range:
1020             lport_range_start, lport_range_stop = lport_range.split('-')
1021
1022         rport_range_start = 0
1023         rport_range_stop = 65535
1024         if rport_range:
1025             rport_range_start, rport_range_stop = rport_range.split('-')
1026
1027         err_msg = f"Failed to add entry to Security Policy Database " \
1028                   f"{spd_id} on host {node[u'host']}"
1029         with PapiSocketExecutor(node, is_async=True) as papi_exec:
1030             for _ in range(n_entries):
1031                 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
1032                     papi_exec, spd_id, next(priority), action, inbound,
1033                     next(sa_id) if sa_id is not None else sa_id,
1034                     proto, next(laddr_range), next(raddr_range), lport_range,
1035                     rport_range, is_ipv6
1036                 )
1037             papi_exec.get_replies(err_msg)
1038
1039     @staticmethod
1040     def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1041         """Create loopback interface and set IP address on VPP node 1 interface
1042         using PAPI.
1043
1044         :param nodes: VPP nodes to create tunnel interfaces.
1045         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1046             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1047             IPv4/IPv6 address (ip2).
1048         :param if1_key: VPP node 1 interface key from topology file.
1049         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1050             interface key from topology file.
1051         :type nodes: dict
1052         :type tun_ips: dict
1053         :type if1_key: str
1054         :type if2_key: str
1055         """
1056         with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1057             # Create loopback interface on DUT1, set it to up state
1058             cmd = u"create_loopback_instance"
1059             args = dict(
1060                 mac_address=0,
1061                 is_specified=False,
1062                 user_instance=0,
1063             )
1064             err_msg = f"Failed to create loopback interface " \
1065                 f"on host {nodes[u'DUT1'][u'host']}"
1066             papi_exec.add(cmd, **args)
1067             loop_sw_if_idx = papi_exec.get_sw_if_index(err_msg)
1068             cmd = u"sw_interface_set_flags"
1069             args = dict(
1070                 sw_if_index=loop_sw_if_idx,
1071                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1072             )
1073             err_msg = f"Failed to set loopback interface state up " \
1074                 f"on host {nodes[u'DUT1'][u'host']}"
1075             papi_exec.add(cmd, **args).get_reply(err_msg)
1076             # Set IP address on VPP node 1 interface
1077             cmd = u"sw_interface_add_del_address"
1078             args = dict(
1079                 sw_if_index=InterfaceUtil.get_interface_index(
1080                     nodes[u"DUT1"], if1_key
1081                 ),
1082                 is_add=True,
1083                 del_all=False,
1084                 prefix=IPUtil.create_prefix_object(
1085                     tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1086                     else 24
1087                 )
1088             )
1089             err_msg = f"Failed to set IP address on interface {if1_key} " \
1090                 f"on host {nodes[u'DUT1'][u'host']}"
1091             papi_exec.add(cmd, **args).get_reply(err_msg)
1092             cmd2 = u"ip_neighbor_add_del"
1093             args2 = dict(
1094                 is_add=1,
1095                 neighbor=dict(
1096                     sw_if_index=Topology.get_interface_sw_index(
1097                         nodes[u"DUT1"], if1_key
1098                     ),
1099                     flags=1,
1100                     mac_address=str(
1101                         Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1102                         if u"DUT2" in nodes.keys()
1103                         else Topology.get_interface_mac(
1104                             nodes[u"TG"], if2_key
1105                         )
1106                     ),
1107                     ip_address=tun_ips[u"ip2"].compressed
1108                 )
1109             )
1110             err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1111             papi_exec.add(cmd2, **args2).get_reply(err_msg)
1112
1113             return loop_sw_if_idx
1114
1115     @staticmethod
1116     def _ipsec_create_tunnel_interfaces_dut1_papi(
1117             nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1118             raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1119         """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1120
1121         Generate random keys and return them (so DUT2 or TG can decrypt).
1122
1123         :param nodes: VPP nodes to create tunnel interfaces.
1124         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1125             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1126             IPv4/IPv6 address (ip2).
1127         :param if1_key: VPP node 1 interface key from topology file.
1128         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1129             interface key from topology file.
1130         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1131         :param crypto_alg: The encryption algorithm name.
1132         :param integ_alg: The integrity algorithm name.
1133         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1134             first tunnel in direction node2->node1.
1135         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1136         :param addr_incr: IP / IPv6 address incremental step.
1137         :param existing_tunnels: Number of tunnel interfaces before creation.
1138             Useful mainly for reconf tests. Default 0.
1139         :type nodes: dict
1140         :type tun_ips: dict
1141         :type if1_key: str
1142         :type if2_key: str
1143         :type n_tunnels: int
1144         :type crypto_alg: CryptoAlg
1145         :type integ_alg: Optional[IntegAlg]
1146         :type raddr_ip2: IPv4Address or IPv6Address
1147         :type addr_incr: int
1148         :type spi_d: dict
1149         :type existing_tunnels: int
1150         :returns: Generated ckeys and ikeys.
1151         :rtype: List[bytes], List[bytes]
1152         """
1153         if not existing_tunnels:
1154             loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1155                 nodes, tun_ips, if1_key, if2_key
1156             )
1157         else:
1158             loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1159                 nodes[u"DUT1"], u"loop0"
1160             )
1161         with PapiSocketExecutor(nodes[u"DUT1"], is_async=True) as papi_exec:
1162             # Configure IP addresses on loop0 interface
1163             cmd = u"sw_interface_add_del_address"
1164             args = dict(
1165                 sw_if_index=loop_sw_if_idx,
1166                 is_add=True,
1167                 del_all=False,
1168                 prefix=None
1169             )
1170             for i in range(existing_tunnels, n_tunnels):
1171                 args[u"prefix"] = IPUtil.create_prefix_object(
1172                     tun_ips[u"ip1"] + i * addr_incr,
1173                     128 if tun_ips[u"ip1"].version == 6 else 32
1174                 )
1175                 papi_exec.add(
1176                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1177                 )
1178             # Configure IPIP tunnel interfaces
1179             cmd = u"ipip_add_tunnel"
1180             ipip_tunnel = dict(
1181                 instance=Constants.BITWISE_NON_ZERO,
1182                 src=None,
1183                 dst=None,
1184                 table_id=0,
1185                 flags=int(
1186                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1187                 ),
1188                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1189                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1190             )
1191             args = dict(
1192                 tunnel=ipip_tunnel
1193             )
1194             ipip_tunnels = [None] * existing_tunnels
1195             for i in range(existing_tunnels, n_tunnels):
1196                 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1197                     tun_ips[u"ip1"] + i * addr_incr
1198                 )
1199                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1200                     tun_ips[u"ip2"]
1201                 )
1202                 papi_exec.add(
1203                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1204                 )
1205             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1206                 f" {nodes[u'DUT1'][u'host']}"
1207             ipip_tunnels.extend(
1208                 [
1209                     reply[u"sw_if_index"]
1210                     for reply in papi_exec.get_replies(err_msg)
1211                     if u"sw_if_index" in reply
1212                 ]
1213             )
1214             # Configure IPSec SAD entries
1215             ckeys = [bytes()] * existing_tunnels
1216             ikeys = [bytes()] * existing_tunnels
1217             cmd = u"ipsec_sad_entry_add"
1218             c_key = dict(
1219                 length=0,
1220                 data=None
1221             )
1222             i_key = dict(
1223                 length=0,
1224                 data=None
1225             )
1226             sad_entry = dict(
1227                 sad_id=None,
1228                 spi=None,
1229                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1230                 crypto_algorithm=crypto_alg.alg_int_repr,
1231                 crypto_key=c_key,
1232                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1233                 integrity_key=i_key,
1234                 flags=None,
1235                 tunnel=dict(
1236                     src=0,
1237                     dst=0,
1238                     table_id=0,
1239                     encap_decap_flags=int(
1240                         TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1241                     ),
1242                     dscp=int(IpDscp.IP_API_DSCP_CS0),
1243                 ),
1244                 salt=0,
1245                 udp_src_port=IPSEC_UDP_PORT_NONE,
1246                 udp_dst_port=IPSEC_UDP_PORT_NONE,
1247             )
1248             args = dict(entry=sad_entry)
1249             for i in range(existing_tunnels, n_tunnels):
1250                 ckeys.append(
1251                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1252                 )
1253                 ikeys.append(
1254                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1255                 )
1256                 # SAD entry for outband / tx path
1257                 args[u"entry"][u"sad_id"] = i
1258                 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1259
1260                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1261                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1262                 if integ_alg:
1263                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1264                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1265                 args[u"entry"][u"flags"] = int(
1266                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1267                 )
1268                 papi_exec.add(
1269                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1270                 )
1271                 # SAD entry for inband / rx path
1272                 args[u"entry"][u"sad_id"] = 100000 + i
1273                 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1274
1275                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1276                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1277                 if integ_alg:
1278                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1279                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1280                 args[u"entry"][u"flags"] = int(
1281                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1282                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1283                 )
1284                 papi_exec.add(
1285                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1286                 )
1287             err_msg = f"Failed to add IPsec SAD entries on host" \
1288                 f" {nodes[u'DUT1'][u'host']}"
1289             papi_exec.get_replies(err_msg)
1290             # Add protection for tunnels with IPSEC
1291             cmd = u"ipsec_tunnel_protect_update"
1292             n_hop = dict(
1293                 address=0,
1294                 via_label=MPLS_LABEL_INVALID,
1295                 obj_id=Constants.BITWISE_NON_ZERO
1296             )
1297             ipsec_tunnel_protect = dict(
1298                 sw_if_index=None,
1299                 nh=n_hop,
1300                 sa_out=None,
1301                 n_sa_in=1,
1302                 sa_in=None
1303             )
1304             args = dict(
1305                 tunnel=ipsec_tunnel_protect
1306             )
1307             for i in range(existing_tunnels, n_tunnels):
1308                 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1309                 args[u"tunnel"][u"sa_out"] = i
1310                 args[u"tunnel"][u"sa_in"] = [100000 + i]
1311                 papi_exec.add(
1312                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1313                 )
1314             err_msg = f"Failed to add protection for tunnels with IPSEC " \
1315                 f"on host {nodes[u'DUT1'][u'host']}"
1316             papi_exec.get_replies(err_msg)
1317
1318             # Configure unnumbered interfaces
1319             cmd = u"sw_interface_set_unnumbered"
1320             args = dict(
1321                 is_add=True,
1322                 sw_if_index=InterfaceUtil.get_interface_index(
1323                     nodes[u"DUT1"], if1_key
1324                 ),
1325                 unnumbered_sw_if_index=0
1326             )
1327             for i in range(existing_tunnels, n_tunnels):
1328                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1329                 papi_exec.add(
1330                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1331                 )
1332             # Set interfaces up
1333             cmd = u"sw_interface_set_flags"
1334             args = dict(
1335                 sw_if_index=0,
1336                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1337             )
1338             for i in range(existing_tunnels, n_tunnels):
1339                 args[u"sw_if_index"] = ipip_tunnels[i]
1340                 papi_exec.add(
1341                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1342                 )
1343             # Configure IP routes
1344             cmd = u"ip_route_add_del"
1345             args = dict(
1346                 is_add=1,
1347                 is_multipath=0,
1348                 route=None
1349             )
1350             for i in range(existing_tunnels, n_tunnels):
1351                 args[u"route"] = IPUtil.compose_vpp_route_structure(
1352                     nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1353                     prefix_len=128 if raddr_ip2.version == 6 else 32,
1354                     interface=ipip_tunnels[i]
1355                 )
1356                 papi_exec.add(
1357                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1358                 )
1359             err_msg = f"Failed to add IP routes on host " \
1360                 f"{nodes[u'DUT1'][u'host']}"
1361             papi_exec.get_replies(err_msg)
1362
1363         return ckeys, ikeys
1364
1365     @staticmethod
1366     def _ipsec_create_tunnel_interfaces_dut2_papi(
1367             nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1368             ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1369         """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1370
1371         This method accesses keys generated by DUT1 method
1372         and does not return anything.
1373
1374         :param nodes: VPP nodes to create tunnel interfaces.
1375         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1376             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1377             IPv4/IPv6 address (ip2).
1378         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1379             interface key from topology file.
1380         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1381         :param crypto_alg: The encryption algorithm name.
1382         :param ckeys: List of encryption keys.
1383         :param integ_alg: The integrity algorithm name.
1384         :param ikeys: List of integrity keys.
1385         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1386         :param addr_incr: IP / IPv6 address incremental step.
1387         :param existing_tunnels: Number of tunnel interfaces before creation.
1388             Useful mainly for reconf tests. Default 0.
1389         :type nodes: dict
1390         :type tun_ips: dict
1391         :type if2_key: str
1392         :type n_tunnels: int
1393         :type crypto_alg: CryptoAlg
1394         :type ckeys: Sequence[bytes]
1395         :type integ_alg: Optional[IntegAlg]
1396         :type ikeys: Sequence[bytes]
1397         :type addr_incr: int
1398         :type spi_d: dict
1399         :type existing_tunnels: int
1400         """
1401         with PapiSocketExecutor(nodes[u"DUT2"], is_async=True) as papi_exec:
1402             if not existing_tunnels:
1403                 # Set IP address on VPP node 2 interface
1404                 cmd = u"sw_interface_add_del_address"
1405                 args = dict(
1406                     sw_if_index=InterfaceUtil.get_interface_index(
1407                         nodes[u"DUT2"], if2_key
1408                     ),
1409                     is_add=True,
1410                     del_all=False,
1411                     prefix=IPUtil.create_prefix_object(
1412                         tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1413                         else 24
1414                     )
1415                 )
1416                 err_msg = f"Failed to set IP address on interface {if2_key} " \
1417                     f"on host {nodes[u'DUT2'][u'host']}"
1418                 papi_exec.add(cmd, **args).get_replies(err_msg)
1419             # Configure IPIP tunnel interfaces
1420             cmd = u"ipip_add_tunnel"
1421             ipip_tunnel = dict(
1422                 instance=Constants.BITWISE_NON_ZERO,
1423                 src=None,
1424                 dst=None,
1425                 table_id=0,
1426                 flags=int(
1427                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1428                 ),
1429                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1430                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1431             )
1432             args = dict(
1433                 tunnel=ipip_tunnel
1434             )
1435             ipip_tunnels = [None] * existing_tunnels
1436             for i in range(existing_tunnels, n_tunnels):
1437                 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1438                     tun_ips[u"ip2"]
1439                 )
1440                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1441                     tun_ips[u"ip1"] + i * addr_incr
1442                 )
1443                 papi_exec.add(
1444                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1445                 )
1446             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1447                 f" {nodes[u'DUT2'][u'host']}"
1448             ipip_tunnels.extend(
1449                 [
1450                     reply[u"sw_if_index"]
1451                     for reply in papi_exec.get_replies(err_msg)
1452                     if u"sw_if_index" in reply
1453                 ]
1454             )
1455             # Configure IPSec SAD entries
1456             cmd = u"ipsec_sad_entry_add"
1457             c_key = dict(
1458                 length=0,
1459                 data=None
1460             )
1461             i_key = dict(
1462                 length=0,
1463                 data=None
1464             )
1465             sad_entry = dict(
1466                 sad_id=None,
1467                 spi=None,
1468                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1469                 crypto_algorithm=crypto_alg.alg_int_repr,
1470                 crypto_key=c_key,
1471                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1472                 integrity_key=i_key,
1473                 flags=None,
1474                 tunnel=dict(
1475                     src=0,
1476                     dst=0,
1477                     table_id=0,
1478                     encap_decap_flags=int(
1479                         TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1480                     ),
1481                     dscp=int(IpDscp.IP_API_DSCP_CS0),
1482                 ),
1483                 salt=0,
1484                 udp_src_port=IPSEC_UDP_PORT_NONE,
1485                 udp_dst_port=IPSEC_UDP_PORT_NONE,
1486             )
1487             args = dict(entry=sad_entry)
1488             for i in range(existing_tunnels, n_tunnels):
1489                 ckeys.append(
1490                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1491                 )
1492                 ikeys.append(
1493                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1494                 )
1495                 # SAD entry for outband / tx path
1496                 args[u"entry"][u"sad_id"] = 100000 + i
1497                 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1498
1499                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1500                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1501                 if integ_alg:
1502                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1503                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1504                 args[u"entry"][u"flags"] = int(
1505                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1506                 )
1507                 papi_exec.add(
1508                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1509                 )
1510                 # SAD entry for inband / rx path
1511                 args[u"entry"][u"sad_id"] = i
1512                 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1513
1514                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1515                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1516                 if integ_alg:
1517                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1518                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1519                 args[u"entry"][u"flags"] = int(
1520                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1521                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1522                 )
1523                 papi_exec.add(
1524                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1525                 )
1526             err_msg = f"Failed to add IPsec SAD entries on host" \
1527                 f" {nodes[u'DUT2'][u'host']}"
1528             papi_exec.get_replies(err_msg)
1529             # Add protection for tunnels with IPSEC
1530             cmd = u"ipsec_tunnel_protect_update"
1531             n_hop = dict(
1532                 address=0,
1533                 via_label=MPLS_LABEL_INVALID,
1534                 obj_id=Constants.BITWISE_NON_ZERO
1535             )
1536             ipsec_tunnel_protect = dict(
1537                 sw_if_index=None,
1538                 nh=n_hop,
1539                 sa_out=None,
1540                 n_sa_in=1,
1541                 sa_in=None
1542             )
1543             args = dict(
1544                 tunnel=ipsec_tunnel_protect
1545             )
1546             for i in range(existing_tunnels, n_tunnels):
1547                 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1548                 args[u"tunnel"][u"sa_out"] = 100000 + i
1549                 args[u"tunnel"][u"sa_in"] = [i]
1550                 papi_exec.add(
1551                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1552                 )
1553             err_msg = f"Failed to add protection for tunnels with IPSEC " \
1554                 f"on host {nodes[u'DUT2'][u'host']}"
1555             papi_exec.get_replies(err_msg)
1556
1557             if not existing_tunnels:
1558                 # Configure IP route
1559                 cmd = u"ip_route_add_del"
1560                 route = IPUtil.compose_vpp_route_structure(
1561                     nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1562                     prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1563                     interface=if2_key,
1564                     gateway=(tun_ips[u"ip2"] - 1).compressed
1565                 )
1566                 args = dict(
1567                     is_add=1,
1568                     is_multipath=0,
1569                     route=route
1570                 )
1571                 papi_exec.add(cmd, **args)
1572             # Configure unnumbered interfaces
1573             cmd = u"sw_interface_set_unnumbered"
1574             args = dict(
1575                 is_add=True,
1576                 sw_if_index=InterfaceUtil.get_interface_index(
1577                     nodes[u"DUT2"], if2_key
1578                 ),
1579                 unnumbered_sw_if_index=0
1580             )
1581             for i in range(existing_tunnels, n_tunnels):
1582                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1583                 papi_exec.add(
1584                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1585                 )
1586             # Set interfaces up
1587             cmd = u"sw_interface_set_flags"
1588             args = dict(
1589                 sw_if_index=0,
1590                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1591             )
1592             for i in range(existing_tunnels, n_tunnels):
1593                 args[u"sw_if_index"] = ipip_tunnels[i]
1594                 papi_exec.add(
1595                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1596                 )
1597             # Configure IP routes
1598             cmd = u"ip_route_add_del"
1599             args = dict(
1600                 is_add=1,
1601                 is_multipath=0,
1602                 route=None
1603             )
1604             for i in range(existing_tunnels, n_tunnels):
1605                 args[u"route"] = IPUtil.compose_vpp_route_structure(
1606                     nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1607                     prefix_len=128 if raddr_ip1.version == 6 else 32,
1608                     interface=ipip_tunnels[i]
1609                 )
1610                 papi_exec.add(
1611                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1612                 )
1613             err_msg = f"Failed to add IP routes " \
1614                 f"on host {nodes[u'DUT2'][u'host']}"
1615             papi_exec.get_replies(err_msg)
1616
1617     @staticmethod
1618     def vpp_ipsec_create_tunnel_interfaces(
1619             nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1620             n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1621             existing_tunnels=0, return_keys=False):
1622         """Create multiple IPsec tunnel interfaces between two VPP nodes.
1623
1624         Some deployments (e.g. devicetest) need to know the generated keys.
1625         But other deployments (e.g. scale perf test) would get spammed
1626         if we returned keys every time.
1627
1628         :param nodes: VPP nodes to create tunnel interfaces.
1629         :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1630             address.
1631         :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1632             address.
1633         :param if1_key: VPP node 1 interface key from topology file.
1634         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1635             interface key from topology file.
1636         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1637         :param crypto_alg: The encryption algorithm name.
1638         :param integ_alg: The integrity algorithm name.
1639         :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1640             first tunnel in direction node1->node2.
1641         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1642             first tunnel in direction node2->node1.
1643         :param raddr_range: Mask specifying range of Policy selector Remote
1644             IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1645             and to 128 in case of IPv6.
1646         :param existing_tunnels: Number of tunnel interfaces before creation.
1647             Useful mainly for reconf tests. Default 0.
1648         :param return_keys: Whether generated keys should be returned.
1649         :type nodes: dict
1650         :type tun_if1_ip_addr: str
1651         :type tun_if2_ip_addr: str
1652         :type if1_key: str
1653         :type if2_key: str
1654         :type n_tunnels: int
1655         :type crypto_alg: CryptoAlg
1656         :type integ_alg: Optonal[IntegAlg]
1657         :type raddr_ip1: string
1658         :type raddr_ip2: string
1659         :type raddr_range: int
1660         :type existing_tunnels: int
1661         :type return_keys: bool
1662         :returns: Ckeys, ikeys, spi_1, spi_2.
1663         :rtype: Optional[List[bytes], List[bytes], int, int]
1664         """
1665         n_tunnels = int(n_tunnels)
1666         existing_tunnels = int(existing_tunnels)
1667         spi_d = dict(
1668             spi_1=100000,
1669             spi_2=200000
1670         )
1671         tun_ips = dict(
1672             ip1=ip_address(tun_if1_ip_addr),
1673             ip2=ip_address(tun_if2_ip_addr)
1674         )
1675         raddr_ip1 = ip_address(raddr_ip1)
1676         raddr_ip2 = ip_address(raddr_ip2)
1677         addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1678             else 1 << (32 - raddr_range)
1679
1680         ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1681             nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1682             integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1683         )
1684         if u"DUT2" in nodes.keys():
1685             IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1686                 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1687                 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1688                 existing_tunnels
1689             )
1690
1691         if return_keys:
1692             return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1693         return None
1694
1695     @staticmethod
1696     def _create_ipsec_script_files(dut, instances):
1697         """Create script files for configuring IPsec in containers
1698
1699         :param dut: DUT node on which to create the script files
1700         :param instances: number of containers on DUT node
1701         :type dut: string
1702         :type instances: int
1703         """
1704         scripts = []
1705         for cnf in range(0, instances):
1706             script_filename = (
1707                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1708             )
1709             scripts.append(open(script_filename, 'w'))
1710         return scripts
1711
1712     @staticmethod
1713     def _close_and_copy_ipsec_script_files(
1714             dut, nodes, instances, scripts):
1715         """Close created scripts and copy them to containers
1716
1717         :param dut: DUT node on which to create the script files
1718         :param nodes: VPP nodes
1719         :param instances: number of containers on DUT node
1720         :param scripts: dictionary holding the script files
1721         :type dut: string
1722         :type nodes: dict
1723         :type instances: int
1724         :type scripts: dict
1725         """
1726         for cnf in range(0, instances):
1727             scripts[cnf].close()
1728             script_filename = (
1729                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1730             )
1731             scp_node(nodes[dut], script_filename, script_filename)
1732
1733
1734     @staticmethod
1735     def vpp_ipsec_create_tunnel_interfaces_in_containers(
1736             nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1737             raddr_ip1, raddr_ip2, raddr_range, n_instances):
1738         """Create multiple IPsec tunnel interfaces between two VPP nodes.
1739
1740         :param nodes: VPP nodes to create tunnel interfaces.
1741         :param if1_ip_addr: VPP node 1 interface IP4 address.
1742         :param if2_ip_addr: VPP node 2 interface IP4 address.
1743         :param n_tunnels: Number of tunnell interfaces to create.
1744         :param crypto_alg: The encryption algorithm name.
1745         :param integ_alg: The integrity algorithm name.
1746         :param raddr_ip1: Policy selector remote IPv4 start address for the
1747             first tunnel in direction node1->node2.
1748         :param raddr_ip2: Policy selector remote IPv4 start address for the
1749             first tunnel in direction node2->node1.
1750         :param raddr_range: Mask specifying range of Policy selector Remote
1751             IPv4 addresses. Valid values are from 1 to 32.
1752         :param n_instances: Number of containers.
1753         :type nodes: dict
1754         :type if1_ip_addr: str
1755         :type if2_ip_addr: str
1756         :type n_tunnels: int
1757         :type crypto_alg: CryptoAlg
1758         :type integ_alg: Optional[IntegAlg]
1759         :type raddr_ip1: string
1760         :type raddr_ip2: string
1761         :type raddr_range: int
1762         :type n_instances: int
1763         """
1764         spi_1 = 100000
1765         spi_2 = 200000
1766         addr_incr = 1 << (32 - raddr_range)
1767
1768         dut1_scripts = IPsecUtil._create_ipsec_script_files(
1769             u"DUT1", n_instances
1770         )
1771         dut2_scripts = IPsecUtil._create_ipsec_script_files(
1772             u"DUT2", n_instances
1773         )
1774
1775         for cnf in range(0, n_instances):
1776             dut1_scripts[cnf].write(
1777                 u"create loopback interface\n"
1778                 u"set interface state loop0 up\n\n"
1779             )
1780             dut2_scripts[cnf].write(
1781                 f"ip route add {if1_ip_addr}/8 via "
1782                 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1783             )
1784
1785         for tnl in range(0, n_tunnels):
1786             cnf = tnl % n_instances
1787             ckey = getattr(
1788                 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1789             )
1790             integ = u""
1791             ikey = getattr(
1792                 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1793             )
1794             if integ_alg:
1795                 integ = (
1796                     f"integ-alg {integ_alg.alg_name} "
1797                     f"local-integ-key {ikey} "
1798                     f"remote-integ-key {ikey} "
1799                 )
1800             # Configure tunnel end point(s) on left side
1801             dut1_scripts[cnf].write(
1802                 u"set interface ip address loop0 "
1803                 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1804                 f"create ipsec tunnel "
1805                 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1806                 f"local-spi {spi_1 + tnl} "
1807                 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1808                 f"remote-spi {spi_2 + tnl} "
1809                 f"crypto-alg {crypto_alg.alg_name} "
1810                 f"local-crypto-key {ckey} "
1811                 f"remote-crypto-key {ckey} "
1812                 f"instance {tnl // n_instances} "
1813                 f"salt 0x0 "
1814                 f"{integ} \n"
1815                 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1816                 f"set interface state ipip{tnl // n_instances} up\n"
1817                 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1818                 f"via ipip{tnl // n_instances}\n\n"
1819             )
1820             # Configure tunnel end point(s) on right side
1821             dut2_scripts[cnf].write(
1822                 f"set ip neighbor memif1/{cnf + 1} "
1823                 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1824                 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1825                 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1826                 f"local-spi {spi_2 + tnl} "
1827                 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1828                 f"remote-spi {spi_1 + tnl} "
1829                 f"crypto-alg {crypto_alg.alg_name} "
1830                 f"local-crypto-key {ckey} "
1831                 f"remote-crypto-key {ckey} "
1832                 f"instance {tnl // n_instances} "
1833                 f"salt 0x0 "
1834                 f"{integ}\n"
1835                 f"set interface unnumbered ipip{tnl // n_instances} "
1836                 f"use memif1/{cnf + 1}\n"
1837                 f"set interface state ipip{tnl // n_instances} up\n"
1838                 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1839                 f"via ipip{tnl // n_instances}\n\n"
1840             )
1841
1842         IPsecUtil._close_and_copy_ipsec_script_files(
1843             u"DUT1", nodes, n_instances, dut1_scripts)
1844         IPsecUtil._close_and_copy_ipsec_script_files(
1845             u"DUT2", nodes, n_instances, dut2_scripts)
1846
1847     @staticmethod
1848     def vpp_ipsec_add_multiple_tunnels(
1849             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1850             tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range,
1851             tunnel_addr_incr=True):
1852         """Create multiple IPsec tunnels between two VPP nodes.
1853
1854         :param nodes: VPP nodes to create tunnels.
1855         :param interface1: Interface name or sw_if_index on node 1.
1856         :param interface2: Interface name or sw_if_index on node 2.
1857         :param n_tunnels: Number of tunnels to create.
1858         :param crypto_alg: The encryption algorithm name.
1859         :param integ_alg: The integrity algorithm name.
1860         :param tunnel_ip1: Tunnel node1 IPv4 address.
1861         :param tunnel_ip2: Tunnel node2 IPv4 address.
1862         :param raddr_ip1: Policy selector remote IPv4 start address for the
1863             first tunnel in direction node1->node2.
1864         :param raddr_ip2: Policy selector remote IPv4 start address for the
1865             first tunnel in direction node2->node1.
1866         :param raddr_range: Mask specifying range of Policy selector Remote
1867             IPv4 addresses. Valid values are from 1 to 32.
1868         :param tunnel_addr_incr: Enable or disable tunnel IP address
1869             incremental step.
1870         :type nodes: dict
1871         :type interface1: str or int
1872         :type interface2: str or int
1873         :type n_tunnels: int
1874         :type crypto_alg: CryptoAlg
1875         :type integ_alg: Optional[IntegAlg]
1876         :type tunnel_ip1: str
1877         :type tunnel_ip2: str
1878         :type raddr_ip1: string
1879         :type raddr_ip2: string
1880         :type raddr_range: int
1881         :type tunnel_addr_incr: bool
1882         """
1883         spd_id = 1
1884         p_hi = 100
1885         p_lo = 10
1886         sa_id_1 = 100000
1887         sa_id_2 = 200000
1888         spi_1 = 300000
1889         spi_2 = 400000
1890
1891         crypto_key = gen_key(
1892             IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1893         ).decode()
1894         integ_key = gen_key(
1895             IPsecUtil.get_integ_alg_key_len(integ_alg)
1896         ).decode() if integ_alg else u""
1897
1898         rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
1899             if u"DUT2" in nodes.keys() \
1900             else Topology.get_interface_mac(nodes[u"TG"], interface2)
1901         IPsecUtil.vpp_ipsec_set_ip_route(
1902             nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1903             interface1, raddr_range, rmac)
1904
1905         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1906         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1907
1908         addr_incr = 1 << (128 - 96) if ip_address(tunnel_ip1).version == 6 \
1909             else 1 << (32 - 24)
1910         for i in range(n_tunnels//(addr_incr**2)+1):
1911             dut1_local_outbound_range = \
1912                 ip_network(f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8",
1913                 False).with_prefixlen
1914             dut1_remote_outbound_range = \
1915                 ip_network(f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8",
1916                 False).with_prefixlen
1917
1918             IPsecUtil.vpp_ipsec_add_spd_entry(
1919                 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1920                 proto=50, laddr_range=dut1_local_outbound_range,
1921                 raddr_range=dut1_remote_outbound_range
1922             )
1923             IPsecUtil.vpp_ipsec_add_spd_entry(
1924                 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1925                 proto=50, laddr_range=dut1_remote_outbound_range,
1926                 raddr_range=dut1_local_outbound_range
1927             )
1928
1929         IPsecUtil.vpp_ipsec_add_sad_entries(
1930             nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1931             integ_alg, integ_key, tunnel_ip1, tunnel_ip2, tunnel_addr_incr
1932         )
1933
1934         IPsecUtil.vpp_ipsec_add_spd_entries(
1935             nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
1936             action=PolicyAction.PROTECT, inbound=False,
1937             sa_id=ObjIncrement(sa_id_1, 1),
1938             raddr_range=NetworkIncrement(ip_network(raddr_ip2))
1939         )
1940
1941         IPsecUtil.vpp_ipsec_add_sad_entries(
1942             nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1943             integ_alg, integ_key, tunnel_ip2, tunnel_ip1, tunnel_addr_incr
1944         )
1945         IPsecUtil.vpp_ipsec_add_spd_entries(
1946             nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
1947             action=PolicyAction.PROTECT, inbound=True,
1948             sa_id=ObjIncrement(sa_id_2, 1),
1949             raddr_range=NetworkIncrement(ip_network(raddr_ip1))
1950         )
1951
1952         if u"DUT2" in nodes.keys():
1953             rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
1954             IPsecUtil.vpp_ipsec_set_ip_route(
1955                 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1956                 interface2, raddr_range, rmac)
1957
1958             IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1959             IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1960             for i in range(n_tunnels//(addr_incr**2)+1):
1961                 dut2_local_outbound_range = \
1962                     ip_network(f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8",
1963                     False).with_prefixlen
1964                 dut2_remote_outbound_range = \
1965                     ip_network(f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8",
1966                     False).with_prefixlen
1967
1968                 IPsecUtil.vpp_ipsec_add_spd_entry(
1969                     nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
1970                     inbound=False, proto=50, laddr_range=dut2_remote_outbound_range,
1971                     raddr_range=dut2_local_outbound_range
1972                 )
1973                 IPsecUtil.vpp_ipsec_add_spd_entry(
1974                     nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
1975                     inbound=True, proto=50, laddr_range=dut2_local_outbound_range,
1976                     raddr_range=dut2_remote_outbound_range
1977                 )
1978
1979             IPsecUtil.vpp_ipsec_add_sad_entries(
1980                 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
1981                 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2,
1982                 tunnel_addr_incr
1983             )
1984             IPsecUtil.vpp_ipsec_add_spd_entries(
1985                 nodes[u"DUT2"], n_tunnels, spd_id,
1986                 priority=ObjIncrement(p_lo, 0),
1987                 action=PolicyAction.PROTECT, inbound=True,
1988                 sa_id=ObjIncrement(sa_id_1, 1),
1989                 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
1990             )
1991
1992             IPsecUtil.vpp_ipsec_add_sad_entries(
1993                 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
1994                 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1,
1995                 tunnel_addr_incr
1996             )
1997             IPsecUtil.vpp_ipsec_add_spd_entries(
1998                 nodes[u"DUT2"], n_tunnels, spd_id,
1999                 priority=ObjIncrement(p_lo, 0),
2000                 action=PolicyAction.PROTECT, inbound=False,
2001                 sa_id=ObjIncrement(sa_id_2, 1),
2002                 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2003             )
2004
2005     @staticmethod
2006     def vpp_ipsec_show_all(node):
2007         """Run "show ipsec all" debug CLI command.
2008
2009         :param node: Node to run command on.
2010         :type node: dict
2011         """
2012         PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2013
2014     @staticmethod
2015     def show_ipsec_security_association(node):
2016         """Show IPSec security association.
2017
2018         :param node: DUT node.
2019         :type node: dict
2020         """
2021         cmds = [
2022             u"ipsec_sa_v3_dump"
2023         ]
2024         PapiSocketExecutor.dump_and_log(node, cmds)
2025
2026     @staticmethod
2027     def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2028         """Ipsec flow enable rss action.
2029
2030         :param node: DUT node.
2031         :param proto: The flow protocol.
2032         :param type: RSS type.
2033         :param function: RSS function.
2034
2035         :type node: dict
2036         :type proto: str
2037         :type type: str
2038         :type function: str
2039         :returns: flow_index.
2040         """
2041         # TODO: to be fixed to use full PAPI when it is ready in VPP
2042         cmd = f"test flow add src-ip any proto {proto} rss function " \
2043             f"{function} rss types {type}"
2044         stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2045         flow_index = stdout.split()[1]
2046
2047         return flow_index
2048
2049     @staticmethod
2050     def vpp_create_ipsec_flows_on_dut(
2051             node, n_flows, rx_queues, spi_start, interface):
2052         """Create mutiple ipsec flows and enable flows onto interface.
2053
2054         :param node: DUT node.
2055         :param n_flows: Number of flows to create.
2056         :param rx_queues: NUmber of RX queues.
2057         :param spi_start: The start spi.
2058         :param interface: Name of the interface.
2059
2060         :type node: dict
2061         :type n_flows: int
2062         :type rx_queues: int
2063         :type spi_start: int
2064         :type interface: str
2065         :returns: flow_index.
2066         """
2067
2068         for i in range(0, n_flows):
2069             rx_queue = i%rx_queues
2070
2071             spi = spi_start + i
2072             flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2073                     node, "ESP", spi, "redirect-to-queue", value=rx_queue)
2074             FlowUtil.vpp_flow_enable(node, interface, flow_index)