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