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