Add more reconf tests, for IPsec
[csit.git] / resources / libraries / python / IPsecUtil.py
1 # Copyright (c) 2020 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.IPUtil import IPUtil
26 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
27     InterfaceStatusFlags
28 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
29 from resources.libraries.python.ssh import scp_node
30 from resources.libraries.python.topology import Topology
31 from resources.libraries.python.VatExecutor import VatExecutor
32
33
34 def gen_key(length):
35     """Generate random string as a key.
36
37     :param length: Length of generated payload.
38     :type length: int
39     :returns: The generated payload.
40     :rtype: bytes
41     """
42     return u"".join(
43         choice(ascii_letters) for _ in range(length)
44     ).encode(encoding=u"utf-8")
45
46
47 class PolicyAction(Enum):
48     """Policy actions."""
49     BYPASS = (u"bypass", 0)
50     DISCARD = (u"discard", 1)
51     PROTECT = (u"protect", 3)
52
53     def __init__(self, policy_name, policy_int_repr):
54         self.policy_name = policy_name
55         self.policy_int_repr = policy_int_repr
56
57
58 class CryptoAlg(Enum):
59     """Encryption algorithms."""
60     AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
61     AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
62     AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
63     AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
64
65     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
66         self.alg_name = alg_name
67         self.alg_int_repr = alg_int_repr
68         self.scapy_name = scapy_name
69         self.key_len = key_len
70
71
72 class IntegAlg(Enum):
73     """Integrity algorithm."""
74     SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
75     SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
76
77     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
78         self.alg_name = alg_name
79         self.alg_int_repr = alg_int_repr
80         self.scapy_name = scapy_name
81         self.key_len = key_len
82
83
84 class IPsecProto(IntEnum):
85     """IPsec protocol."""
86     ESP = 1
87     SEC_AH = 0
88
89
90 class IPsecSadFlags(IntEnum):
91     """IPsec Security Association Database flags."""
92     IPSEC_API_SAD_FLAG_NONE = 0
93     IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
94     IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
95
96
97 class IPsecUtil:
98     """IPsec utilities."""
99
100     @staticmethod
101     def policy_action_bypass():
102         """Return policy action bypass.
103
104         :returns: PolicyAction enum BYPASS object.
105         :rtype: PolicyAction
106         """
107         return PolicyAction.BYPASS
108
109     @staticmethod
110     def policy_action_discard():
111         """Return policy action discard.
112
113         :returns: PolicyAction enum DISCARD object.
114         :rtype: PolicyAction
115         """
116         return PolicyAction.DISCARD
117
118     @staticmethod
119     def policy_action_protect():
120         """Return policy action protect.
121
122         :returns: PolicyAction enum PROTECT object.
123         :rtype: PolicyAction
124         """
125         return PolicyAction.PROTECT
126
127     @staticmethod
128     def crypto_alg_aes_cbc_128():
129         """Return encryption algorithm aes-cbc-128.
130
131         :returns: CryptoAlg enum AES_CBC_128 object.
132         :rtype: CryptoAlg
133         """
134         return CryptoAlg.AES_CBC_128
135
136     @staticmethod
137     def crypto_alg_aes_cbc_256():
138         """Return encryption algorithm aes-cbc-256.
139
140         :returns: CryptoAlg enum AES_CBC_256 object.
141         :rtype: CryptoAlg
142         """
143         return CryptoAlg.AES_CBC_256
144
145     @staticmethod
146     def crypto_alg_aes_gcm_128():
147         """Return encryption algorithm aes-gcm-128.
148
149         :returns: CryptoAlg enum AES_GCM_128 object.
150         :rtype: CryptoAlg
151         """
152         return CryptoAlg.AES_GCM_128
153
154     @staticmethod
155     def crypto_alg_aes_gcm_256():
156         """Return encryption algorithm aes-gcm-256.
157
158         :returns: CryptoAlg enum AES_GCM_128 object.
159         :rtype: CryptoAlg
160         """
161         return CryptoAlg.AES_GCM_256
162
163     @staticmethod
164     def get_crypto_alg_key_len(crypto_alg):
165         """Return encryption algorithm key length.
166
167         :param crypto_alg: Encryption algorithm.
168         :type crypto_alg: CryptoAlg
169         :returns: Key length.
170         :rtype: int
171         """
172         return crypto_alg.key_len
173
174     @staticmethod
175     def get_crypto_alg_scapy_name(crypto_alg):
176         """Return encryption algorithm scapy name.
177
178         :param crypto_alg: Encryption algorithm.
179         :type crypto_alg: CryptoAlg
180         :returns: Algorithm scapy name.
181         :rtype: str
182         """
183         return crypto_alg.scapy_name
184
185     @staticmethod
186     def integ_alg_sha_256_128():
187         """Return integrity algorithm SHA-256-128.
188
189         :returns: IntegAlg enum SHA_256_128 object.
190         :rtype: IntegAlg
191         """
192         return IntegAlg.SHA_256_128
193
194     @staticmethod
195     def integ_alg_sha_512_256():
196         """Return integrity algorithm SHA-512-256.
197
198         :returns: IntegAlg enum SHA_512_256 object.
199         :rtype: IntegAlg
200         """
201         return IntegAlg.SHA_512_256
202
203     @staticmethod
204     def get_integ_alg_key_len(integ_alg):
205         """Return integrity algorithm key length.
206
207         :param integ_alg: Integrity algorithm.
208         :type integ_alg: IntegAlg
209         :returns: Key length.
210         :rtype: int
211         """
212         return integ_alg.key_len
213
214     @staticmethod
215     def get_integ_alg_scapy_name(integ_alg):
216         """Return integrity algorithm scapy name.
217
218         :param integ_alg: Integrity algorithm.
219         :type integ_alg: IntegAlg
220         :returns: Algorithm scapy name.
221         :rtype: str
222         """
223         return integ_alg.scapy_name
224
225     @staticmethod
226     def ipsec_proto_esp():
227         """Return IPSec protocol ESP.
228
229         :returns: IPsecProto enum ESP object.
230         :rtype: IPsecProto
231         """
232         return int(IPsecProto.ESP)
233
234     @staticmethod
235     def ipsec_proto_ah():
236         """Return IPSec protocol AH.
237
238         :returns: IPsecProto enum AH object.
239         :rtype: IPsecProto
240         """
241         return int(IPsecProto.SEC_AH)
242
243     @staticmethod
244     def vpp_ipsec_select_backend(node, protocol, index=1):
245         """Select IPsec backend.
246
247         :param node: VPP node to select IPsec backend on.
248         :param protocol: IPsec protocol.
249         :param index: Backend index.
250         :type node: dict
251         :type protocol: IPsecProto
252         :type index: int
253         :raises RuntimeError: If failed to select IPsec backend or if no API
254             reply received.
255         """
256         cmd = u"ipsec_select_backend"
257         err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
258         args = dict(
259             protocol=protocol,
260             index=index
261         )
262         with PapiSocketExecutor(node) as papi_exec:
263             papi_exec.add(cmd, **args).get_reply(err_msg)
264
265     @staticmethod
266     def vpp_ipsec_add_sad_entry(
267             node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
268             integ_key=u"", tunnel_src=None, tunnel_dst=None):
269         """Create Security Association Database entry on the VPP node.
270
271         :param node: VPP node to add SAD entry on.
272         :param sad_id: SAD entry ID.
273         :param spi: Security Parameter Index of this SAD entry.
274         :param crypto_alg: The encryption algorithm name.
275         :param crypto_key: The encryption key string.
276         :param integ_alg: The integrity algorithm name.
277         :param integ_key: The integrity key string.
278         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
279             specified ESP transport mode is used.
280         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
281             not specified ESP transport mode is used.
282         :type node: dict
283         :type sad_id: int
284         :type spi: int
285         :type crypto_alg: CryptoAlg
286         :type crypto_key: str
287         :type integ_alg: IntegAlg
288         :type integ_key: str
289         :type tunnel_src: str
290         :type tunnel_dst: str
291         """
292         if isinstance(crypto_key, str):
293             crypto_key = crypto_key.encode(encoding=u"utf-8")
294         if isinstance(integ_key, str):
295             integ_key = integ_key.encode(encoding=u"utf-8")
296         ckey = dict(
297             length=len(crypto_key),
298             data=crypto_key
299         )
300         ikey = dict(
301             length=len(integ_key),
302             data=integ_key if integ_key else 0
303         )
304
305         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
306         if tunnel_src and tunnel_dst:
307             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
308             src_addr = ip_address(tunnel_src)
309             dst_addr = ip_address(tunnel_dst)
310             if src_addr.version == 6:
311                 flags = \
312                     flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
313         else:
314             src_addr = u""
315             dst_addr = u""
316
317         cmd = u"ipsec_sad_entry_add_del"
318         err_msg = f"Failed to add Security Association Database entry " \
319             f"on host {node[u'host']}"
320         sad_entry = dict(
321             sad_id=int(sad_id),
322             spi=int(spi),
323             crypto_algorithm=crypto_alg.alg_int_repr,
324             crypto_key=ckey,
325             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
326             integrity_key=ikey,
327             flags=flags,
328             tunnel_src=str(src_addr),
329             tunnel_dst=str(dst_addr),
330             protocol=int(IPsecProto.ESP)
331         )
332         args = dict(
333             is_add=1,
334             entry=sad_entry
335         )
336         with PapiSocketExecutor(node) as papi_exec:
337             papi_exec.add(cmd, **args).get_reply(err_msg)
338
339     @staticmethod
340     def vpp_ipsec_add_sad_entries(
341             node, n_entries, sad_id, spi, crypto_alg, crypto_key,
342             integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
343         """Create multiple Security Association Database entries on VPP node.
344
345         :param node: VPP node to add SAD entry on.
346         :param n_entries: Number of SAD entries to be created.
347         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
348             id incremented by 1.
349         :param spi: Security Parameter Index of first SAD entry. All subsequent
350             SAD entries will have spi incremented by 1.
351         :param crypto_alg: The encryption algorithm name.
352         :param crypto_key: The encryption key string.
353         :param integ_alg: The integrity algorithm name.
354         :param integ_key: The integrity key string.
355         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
356             specified ESP transport mode is used.
357         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
358             not specified ESP transport mode is used.
359         :type node: dict
360         :type n_entries: int
361         :type sad_id: int
362         :type spi: int
363         :type crypto_alg: CryptoAlg
364         :type crypto_key: str
365         :type integ_alg: IntegAlg
366         :type integ_key: str
367         :type tunnel_src: str
368         :type tunnel_dst: str
369         """
370         if isinstance(crypto_key, str):
371             crypto_key = crypto_key.encode(encoding=u"utf-8")
372         if isinstance(integ_key, str):
373             integ_key = integ_key.encode(encoding=u"utf-8")
374         if tunnel_src and tunnel_dst:
375             src_addr = ip_address(tunnel_src)
376             dst_addr = ip_address(tunnel_dst)
377         else:
378             src_addr = u""
379             dst_addr = u""
380
381         addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
382             else 1 << (32 - 24)
383
384         if int(n_entries) > 10:
385             tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
386
387             with open(tmp_filename, 'w') as tmp_file:
388                 for i in range(n_entries):
389                     integ = f"integ-alg {integ_alg.alg_name} " \
390                         f"integ-key {integ_key.hex()}" \
391                         if integ_alg else u""
392                     tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
393                         f"tunnel-dst {dst_addr + i * addr_incr}" \
394                         if tunnel_src and tunnel_dst else u""
395                     conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
396                         f"crypto-alg {crypto_alg.alg_name} " \
397                         f"crypto-key {crypto_key.hex()} " \
398                         f"{integ} {tunnel}\n"
399                     tmp_file.write(conf)
400             vat = VatExecutor()
401             vat.execute_script(
402                 tmp_filename, node, timeout=300, json_out=False,
403                 copy_on_execute=True
404             )
405             os.remove(tmp_filename)
406             return
407
408         ckey = dict(
409             length=len(crypto_key),
410             data=crypto_key
411         )
412         ikey = dict(
413             length=len(integ_key),
414             data=integ_key if integ_key else 0
415         )
416
417         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
418         if tunnel_src and tunnel_dst:
419             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
420             if src_addr.version == 6:
421                 flags = flags | int(
422                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
423                 )
424
425         cmd = u"ipsec_sad_entry_add_del"
426         err_msg = f"Failed to add Security Association Database entry " \
427             f"on host {node[u'host']}"
428
429         sad_entry = dict(
430             sad_id=int(sad_id),
431             spi=int(spi),
432             crypto_algorithm=crypto_alg.alg_int_repr,
433             crypto_key=ckey,
434             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
435             integrity_key=ikey,
436             flags=flags,
437             tunnel_src=str(src_addr),
438             tunnel_dst=str(dst_addr),
439             protocol=int(IPsecProto.ESP)
440         )
441         args = dict(
442             is_add=1,
443             entry=sad_entry
444         )
445         with PapiSocketExecutor(node) as papi_exec:
446             for i in range(n_entries):
447                 args[u"entry"][u"sad_id"] = int(sad_id) + i
448                 args[u"entry"][u"spi"] = int(spi) + i
449                 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
450                     if tunnel_src and tunnel_dst else src_addr
451                 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
452                     if tunnel_src and tunnel_dst else dst_addr
453                 history = bool(not 1 < i < n_entries - 2)
454                 papi_exec.add(cmd, history=history, **args)
455             papi_exec.get_replies(err_msg)
456
457     @staticmethod
458     def vpp_ipsec_set_ip_route(
459             node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
460             raddr_range):
461         """Set IP address and route on interface.
462
463         :param node: VPP node to add config on.
464         :param n_tunnels: Number of tunnels to create.
465         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
466         :param traffic_addr: Traffic destination IP address to route.
467         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
468         :param interface: Interface key on node 1.
469         :param raddr_range: Mask specifying range of Policy selector Remote IP
470             addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
471             in case of IPv6.
472         :type node: dict
473         :type n_tunnels: int
474         :type tunnel_src: str
475         :type traffic_addr: str
476         :type tunnel_dst: str
477         :type interface: str
478         :type raddr_range: int
479         """
480         laddr = ip_address(tunnel_src)
481         raddr = ip_address(tunnel_dst)
482         taddr = ip_address(traffic_addr)
483         addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
484             else 1 << (32 - raddr_range)
485
486         if int(n_tunnels) > 10:
487             tmp_filename = u"/tmp/ipsec_set_ip.script"
488
489             with open(tmp_filename, 'w') as tmp_file:
490                 if_name = Topology.get_interface_name(node, interface)
491                 for i in range(n_tunnels):
492                     conf = f"exec set interface ip address {if_name} " \
493                         f"{laddr + i * addr_incr}/{raddr_range}\n" \
494                         f"exec ip route add {taddr + i}/" \
495                         f"{128 if taddr.version == 6 else 32} " \
496                         f"via {raddr + i * addr_incr} {if_name}\n"
497                     tmp_file.write(conf)
498             vat = VatExecutor()
499             vat.execute_script(
500                 tmp_filename, node, timeout=300, json_out=False,
501                 copy_on_execute=True
502             )
503             os.remove(tmp_filename)
504             return
505
506         cmd1 = u"sw_interface_add_del_address"
507         args1 = dict(
508             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
509             is_add=True,
510             del_all=False,
511             prefix=None
512         )
513         cmd2 = u"ip_route_add_del"
514         args2 = dict(
515             is_add=1,
516             is_multipath=0,
517             route=None
518         )
519         err_msg = f"Failed to configure IP addresses and IP routes " \
520             f"on interface {interface} on host {node[u'host']}"
521
522         with PapiSocketExecutor(node) as papi_exec:
523             for i in range(n_tunnels):
524                 args1[u"prefix"] = IPUtil.create_prefix_object(
525                     laddr + i * addr_incr, raddr_range
526                 )
527                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
528                     node, taddr + i,
529                     prefix_len=128 if taddr.version == 6 else 32,
530                     interface=interface, gateway=raddr + i * addr_incr
531                 )
532                 history = bool(not 1 < i < n_tunnels - 2)
533                 papi_exec.add(cmd1, history=history, **args1).\
534                     add(cmd2, history=history, **args2)
535             papi_exec.get_replies(err_msg)
536
537     @staticmethod
538     def vpp_ipsec_add_spd(node, spd_id):
539         """Create Security Policy Database on the VPP node.
540
541         :param node: VPP node to add SPD on.
542         :param spd_id: SPD ID.
543         :type node: dict
544         :type spd_id: int
545         """
546         cmd = u"ipsec_spd_add_del"
547         err_msg = f"Failed to add Security Policy Database " \
548             f"on host {node[u'host']}"
549         args = dict(
550             is_add=1,
551             spd_id=int(spd_id)
552         )
553         with PapiSocketExecutor(node) as papi_exec:
554             papi_exec.add(cmd, **args).get_reply(err_msg)
555
556     @staticmethod
557     def vpp_ipsec_spd_add_if(node, spd_id, interface):
558         """Add interface to the Security Policy Database.
559
560         :param node: VPP node.
561         :param spd_id: SPD ID to add interface on.
562         :param interface: Interface name or sw_if_index.
563         :type node: dict
564         :type spd_id: int
565         :type interface: str or int
566         """
567         cmd = u"ipsec_interface_add_del_spd"
568         err_msg = f"Failed to add interface {interface} to Security Policy " \
569             f"Database {spd_id} on host {node[u'host']}"
570         args = dict(
571             is_add=1,
572             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
573             spd_id=int(spd_id)
574         )
575         with PapiSocketExecutor(node) as papi_exec:
576             papi_exec.add(cmd, **args).get_reply(err_msg)
577
578     @staticmethod
579     def vpp_ipsec_policy_add(
580             node, spd_id, priority, action, inbound=True, sa_id=None,
581             laddr_range=None, raddr_range=None, proto=None, lport_range=None,
582             rport_range=None, is_ipv6=False):
583         """Create Security Policy Database entry on the VPP node.
584
585         :param node: VPP node to add SPD entry on.
586         :param spd_id: SPD ID to add entry on.
587         :param priority: SPD entry priority, higher number = higher priority.
588         :param action: Policy action.
589         :param inbound: If True policy is for inbound traffic, otherwise
590             outbound.
591         :param sa_id: SAD entry ID for protect action.
592         :param laddr_range: Policy selector local IPv4 or IPv6 address range in
593             format IP/prefix or IP/mask. If no mask is provided,
594             it's considered to be /32.
595         :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
596             format IP/prefix or IP/mask. If no mask is provided,
597             it's considered to be /32.
598         :param proto: Policy selector next layer protocol number.
599         :param lport_range: Policy selector local TCP/UDP port range in format
600             <port_start>-<port_end>.
601         :param rport_range: Policy selector remote TCP/UDP port range in format
602             <port_start>-<port_end>.
603         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
604             not defined so it will default to address ::/0, otherwise False.
605         :type node: dict
606         :type spd_id: int
607         :type priority: int
608         :type action: PolicyAction
609         :type inbound: bool
610         :type sa_id: int
611         :type laddr_range: string
612         :type raddr_range: string
613         :type proto: int
614         :type lport_range: string
615         :type rport_range: string
616         :type is_ipv6: bool
617         """
618         if laddr_range is None:
619             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
620
621         if raddr_range is None:
622             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
623
624         cmd = u"ipsec_spd_entry_add_del"
625         err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
626             f"on host {node[u'host']}"
627
628         spd_entry = dict(
629             spd_id=int(spd_id),
630             priority=int(priority),
631             is_outbound=0 if inbound else 1,
632             sa_id=int(sa_id) if sa_id else 0,
633             policy=action.policy_int_repr,
634             protocol=int(proto) if proto else 0,
635             remote_address_start=IPUtil.create_ip_address_object(
636                 ip_network(raddr_range, strict=False).network_address
637             ),
638             remote_address_stop=IPUtil.create_ip_address_object(
639                 ip_network(raddr_range, strict=False).broadcast_address
640             ),
641             local_address_start=IPUtil.create_ip_address_object(
642                 ip_network(laddr_range, strict=False).network_address
643             ),
644             local_address_stop=IPUtil.create_ip_address_object(
645                 ip_network(laddr_range, strict=False).broadcast_address
646             ),
647             remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
648             else 0,
649             remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
650             else 65535,
651             local_port_start=int(lport_range.split(u"-")[0]) if lport_range
652             else 0,
653             local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
654             else 65535
655         )
656         args = dict(
657             is_add=1,
658             entry=spd_entry
659         )
660         with PapiSocketExecutor(node) as papi_exec:
661             papi_exec.add(cmd, **args).get_reply(err_msg)
662
663     @staticmethod
664     def vpp_ipsec_spd_add_entries(
665             node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
666             raddr_range=0):
667         """Create multiple Security Policy Database entries on the VPP node.
668
669         :param node: VPP node to add SPD entries on.
670         :param n_entries: Number of SPD entries to be added.
671         :param spd_id: SPD ID to add entries on.
672         :param priority: SPD entries priority, higher number = higher priority.
673         :param inbound: If True policy is for inbound traffic, otherwise
674             outbound.
675         :param sa_id: SAD entry ID for first entry. Each subsequent entry will
676             SAD entry ID incremented by 1.
677         :param raddr_ip: Policy selector remote IPv4 start address for the first
678             entry. Remote IPv4 end address will be calculated depending on
679             raddr_range parameter. Each subsequent entry will have start address
680             next after IPv4 end address of previous entry.
681         :param raddr_range: Required IP addres range.
682         :type node: dict
683         :type n_entries: int
684         :type spd_id: int
685         :type priority: int
686         :type inbound: bool
687         :type sa_id: int
688         :type raddr_ip: str
689         :type raddr_range: int
690         """
691         raddr_ip = ip_address(raddr_ip)
692         if int(n_entries) > 10:
693             tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
694
695             with open(tmp_filename, 'w') as tmp_file:
696                 for i in range(n_entries):
697                     direction = u'inbound' if inbound else u'outbound'
698                     tunnel = f"exec ipsec policy add spd {spd_id} " \
699                         f"priority {priority} {direction} " \
700                         f"action protect sa {sa_id+i} " \
701                         f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
702                         f"- {raddr_ip + (i  + 1) * raddr_range + i} " \
703                         f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
704                     tmp_file.write(tunnel)
705             VatExecutor().execute_script(
706                 tmp_filename, node, timeout=300, json_out=False,
707                 copy_on_execute=True
708             )
709             os.remove(tmp_filename)
710             return
711
712         laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
713
714         cmd = u"ipsec_spd_entry_add_del"
715         err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
716             f"on host {node[u'host']}"
717
718         spd_entry = dict(
719             spd_id=int(spd_id),
720             priority=int(priority),
721             is_outbound=0 if inbound else 1,
722             sa_id=int(sa_id) if sa_id else 0,
723             policy=IPsecUtil.policy_action_protect().policy_int_repr,
724             protocol=0,
725             remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
726             remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
727             local_address_start=IPUtil.create_ip_address_object(
728                 ip_network(laddr_range, strict=False).network_address
729             ),
730             local_address_stop=IPUtil.create_ip_address_object(
731                 ip_network(laddr_range, strict=False).broadcast_address
732             ),
733             remote_port_start=0,
734             remote_port_stop=65535,
735             local_port_start=0,
736             local_port_stop=65535
737         )
738         args = dict(
739             is_add=1,
740             entry=spd_entry
741         )
742
743         with PapiSocketExecutor(node) as papi_exec:
744             for i in range(n_entries):
745                 args[u"entry"][u"remote_address_start"][u"un"] = \
746                     IPUtil.union_addr(raddr_ip + i)
747                 args[u"entry"][u"remote_address_stop"][u"un"] = \
748                     IPUtil.union_addr(raddr_ip + i)
749                 history = bool(not 1 < i < n_entries - 2)
750                 papi_exec.add(cmd, history=history, **args)
751             papi_exec.get_replies(err_msg)
752
753     @staticmethod
754     def vpp_ipsec_create_tunnel_interfaces(
755             nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
756             crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
757             existing_tunnels=0):
758         """Create multiple IPsec tunnel interfaces between two VPP nodes.
759
760         :param nodes: VPP nodes to create tunnel interfaces.
761         :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
762         :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
763         :param if1_key: VPP node 1 interface key from topology file.
764         :param if2_key: VPP node 2 interface key from topology file.
765         :param n_tunnels: Number of tunnel interfaces to be there at the end.
766         :param crypto_alg: The encryption algorithm name.
767         :param integ_alg: The integrity algorithm name.
768         :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
769             first tunnel in direction node1->node2.
770         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
771             first tunnel in direction node2->node1.
772         :param raddr_range: Mask specifying range of Policy selector Remote
773             IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
774             and to 128 in case of IPv6.
775         :param existing_tunnels: Number of tunnel interfaces before creation.
776             Useful mainly for reconf tests. Default 0.
777         :type nodes: dict
778         :type if1_ip_addr: str
779         :type if2_ip_addr: str
780         :type if1_key: str
781         :type if2_key: str
782         :type n_tunnels: int
783         :type crypto_alg: CryptoAlg
784         :type integ_alg: IntegAlg
785         :type raddr_ip1: string
786         :type raddr_ip2: string
787         :type raddr_range: int
788         :type existing_tunnels: int
789         """
790         n_tunnels = int(n_tunnels)
791         existing_tunnels = int(existing_tunnels)
792         spi_1 = 100000
793         spi_2 = 200000
794         if1_ip = ip_address(if1_ip_addr)
795         if2_ip = ip_address(if2_ip_addr)
796         raddr_ip1 = ip_address(raddr_ip1)
797         raddr_ip2 = ip_address(raddr_ip2)
798         addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
799             else 1 << (32 - raddr_range)
800
801         if n_tunnels - existing_tunnels > 10:
802             tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
803             tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
804             if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
805             if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
806             mask = 96 if if2_ip.version == 6 else 24
807             mask2 = 128 if if2_ip.version == 6 else 32
808             vat = VatExecutor()
809             with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
810                 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
811                 if not existing_tunnels:
812                     tmp_f1.write(
813                         f"exec create loopback interface\n"
814                         f"exec set interface state loop0 up\n"
815                         f"exec set interface ip address "
816                         f"{if1_n} {if2_ip - 1}/{mask}\n"
817                         f"exec set ip neighbor {if1_n} {if2_ip}/{mask2} {rmac}"
818                         f" static\n"
819                     )
820                     tmp_f2.write(
821                         f"exec set interface ip address {if2_n}"
822                         f" {if2_ip}/{mask}\n"
823                     )
824                 for i in range(existing_tunnels, n_tunnels):
825                     ckey = gen_key(
826                         IPsecUtil.get_crypto_alg_key_len(crypto_alg)
827                     ).hex()
828                     if integ_alg:
829                         ikey = gen_key(
830                             IPsecUtil.get_integ_alg_key_len(integ_alg)
831                         ).hex()
832                         integ = f"integ_alg {integ_alg.alg_name} " \
833                             f"local_integ_key {ikey} remote_integ_key {ikey} "
834                     else:
835                         integ = u""
836                     tmp_f1.write(
837                         f"exec set interface ip address loop0 "
838                         f"{if1_ip + i * addr_incr}/32\n"
839                         f"ipsec_tunnel_if_add_del "
840                         f"local_spi {spi_1 + i} remote_spi {spi_2 + i} "
841                         f"crypto_alg {crypto_alg.alg_name} "
842                         f"local_crypto_key {ckey} remote_crypto_key {ckey} "
843                         f"{integ} "
844                         f"local_ip {if1_ip + i * addr_incr} "
845                         f"remote_ip {if2_ip} "
846                         f"instance {i}\n"
847                     )
848                     tmp_f2.write(
849                         f"ipsec_tunnel_if_add_del "
850                         f"local_spi {spi_2 + i} remote_spi {spi_1 + i} "
851                         f"crypto_alg {crypto_alg.alg_name} "
852                         f"local_crypto_key {ckey} remote_crypto_key {ckey} "
853                         f"{integ} "
854                         f"local_ip {if2_ip} "
855                         f"remote_ip {if1_ip + i * addr_incr} "
856                         f"instance {i}\n"
857                     )
858             vat.execute_script(
859                 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
860                 copy_on_execute=True,
861                 history=bool(n_tunnels < 100)
862             )
863             vat.execute_script(
864                 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
865                 copy_on_execute=True,
866                 history=bool(n_tunnels < 100)
867             )
868             os.remove(tmp_fn1)
869             os.remove(tmp_fn2)
870
871             with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
872                 if not existing_tunnels:
873                     tmp_f2.write(
874                         f"exec ip route add {if1_ip}/8 via {if2_ip - 1}"
875                         f" {if2_n}\n"
876                     )
877                 for i in range(existing_tunnels, n_tunnels):
878                     tmp_f1.write(
879                         f"exec set interface unnumbered ipip{i} use {if1_n}\n"
880                         f"exec set interface state ipip{i} up\n"
881                         f"exec ip route add {raddr_ip2 + i}/{mask2} "
882                         f"via ipip{i}\n"
883                     )
884                     tmp_f2.write(
885                         f"exec set interface unnumbered ipip{i} use {if2_n}\n"
886                         f"exec set interface state ipip{i} up\n"
887                         f"exec ip route add {raddr_ip1 + i}/{mask2} "
888                         f"via ipip{i}\n"
889                     )
890             vat.execute_script(
891                 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
892                 copy_on_execute=True,
893                 history=bool(n_tunnels < 100)
894             )
895             vat.execute_script(
896                 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
897                 copy_on_execute=True,
898                 history=bool(n_tunnels < 100)
899             )
900             os.remove(tmp_fn1)
901             os.remove(tmp_fn2)
902             return
903
904         if not existing_tunnels:
905             with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
906                 # Create loopback interface on DUT1, set it to up state
907                 cmd1 = u"create_loopback"
908                 args1 = dict(
909                     mac_address=0
910                 )
911                 err_msg = f"Failed to create loopback interface " \
912                     f"on host {nodes[u'DUT1'][u'host']}"
913                 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
914                     get_sw_if_index(err_msg)
915                 cmd1 = u"sw_interface_set_flags"
916                 args1 = dict(
917                     sw_if_index=loop_sw_if_idx,
918                     flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
919                 )
920                 err_msg = f"Failed to set loopback interface state up " \
921                     f"on host {nodes[u'DUT1'][u'host']}"
922                 papi_exec.add(cmd1, **args1).get_reply(err_msg)
923                 # Set IP address on VPP node 1 interface
924                 cmd1 = u"sw_interface_add_del_address"
925                 args1 = dict(
926                     sw_if_index=InterfaceUtil.get_interface_index(
927                         nodes[u"DUT1"], if1_key
928                     ),
929                     is_add=True,
930                     del_all=False,
931                     prefix=IPUtil.create_prefix_object(
932                         if2_ip - 1, 96 if if2_ip.version == 6 else 24
933                     )
934                 )
935                 err_msg = f"Failed to set IP address on interface {if1_key} " \
936                     f"on host {nodes[u'DUT1'][u'host']}"
937                 papi_exec.add(cmd1, **args1).get_reply(err_msg)
938                 cmd4 = u"ip_neighbor_add_del"
939                 args4 = dict(
940                     is_add=1,
941                     neighbor=dict(
942                         sw_if_index=Topology.get_interface_sw_index(
943                             nodes[u"DUT1"], if1_key
944                         ),
945                         flags=1,
946                         mac_address=str(
947                             Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
948                         ),
949                         ip_address=str(ip_address(if2_ip_addr))
950                     )
951                 )
952                 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
953                 papi_exec.add(cmd4, **args4).get_reply(err_msg)
954         else:
955             # Executor not open, as InterfaceUtil will open its own.
956             loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
957                 nodes[u"DUT1"], u"loop0")
958             cmd1 = u"sw_interface_add_del_address"
959         with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
960             # Configure IPsec tunnel interfaces
961             args1 = dict(
962                 sw_if_index=loop_sw_if_idx,
963                 is_add=True,
964                 del_all=False,
965                 prefix=None
966             )
967             cmd2 = u"ipsec_tunnel_if_add_del"
968             args2 = dict(
969                 is_add=1,
970                 local_ip=None,
971                 remote_ip=None,
972                 local_spi=0,
973                 remote_spi=0,
974                 crypto_alg=crypto_alg.alg_int_repr,
975                 local_crypto_key_len=0,
976                 local_crypto_key=None,
977                 remote_crypto_key_len=0,
978                 remote_crypto_key=None,
979                 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
980                 local_integ_key_len=0,
981                 local_integ_key=None,
982                 remote_integ_key_len=0,
983                 remote_integ_key=None,
984                 tx_table_id=0
985             )
986             err_msg = f"Failed to add IPsec tunnel interfaces " \
987                 f"on host {nodes[u'DUT1'][u'host']}"
988             ipsec_tunnels = [None] * existing_tunnels
989             ckeys = [None] * existing_tunnels
990             ikeys = [None] * existing_tunnels
991             for i in range(existing_tunnels, n_tunnels):
992                 ckeys.append(
993                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
994                 )
995                 if integ_alg:
996                     ikeys.append(
997                         gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
998                     )
999                 args1[u"prefix"] = IPUtil.create_prefix_object(
1000                     if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32
1001                 )
1002                 args2[u"local_spi"] = spi_1 + i
1003                 args2[u"remote_spi"] = spi_2 + i
1004                 args2[u"local_ip"] = IPUtil.create_ip_address_object(
1005                     if1_ip + i * addr_incr
1006                 )
1007                 args2[u"remote_ip"] = IPUtil.create_ip_address_object(if2_ip)
1008                 args2[u"local_crypto_key_len"] = len(ckeys[i])
1009                 args2[u"local_crypto_key"] = ckeys[i]
1010                 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1011                 args2[u"remote_crypto_key"] = ckeys[i]
1012                 if integ_alg:
1013                     args2[u"local_integ_key_len"] = len(ikeys[i])
1014                     args2[u"local_integ_key"] = ikeys[i]
1015                     args2[u"remote_integ_key_len"] = len(ikeys[i])
1016                     args2[u"remote_integ_key"] = ikeys[i]
1017                 history = bool(not 1 < i < n_tunnels - 2)
1018                 papi_exec.add(cmd1, history=history, **args1).\
1019                     add(cmd2, history=history, **args2)
1020             replies = papi_exec.get_replies(err_msg)
1021             for reply in replies:
1022                 if u"sw_if_index" in reply:
1023                     ipsec_tunnels.append(reply[u"sw_if_index"])
1024             # Configure IP routes
1025             cmd1 = u"sw_interface_set_unnumbered"
1026             args1 = dict(
1027                 is_add=True,
1028                 sw_if_index=InterfaceUtil.get_interface_index(
1029                     nodes[u"DUT1"], if1_key
1030                 ),
1031                 unnumbered_sw_if_index=0
1032             )
1033             cmd2 = u"sw_interface_set_flags"
1034             args2 = dict(
1035                 sw_if_index=0,
1036                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1037             )
1038             cmd3 = u"ip_route_add_del"
1039             args3 = dict(
1040                 is_add=1,
1041                 is_multipath=0,
1042                 route=None
1043             )
1044             err_msg = f"Failed to add IP routes " \
1045                 f"on host {nodes[u'DUT1'][u'host']}"
1046             for i in range(existing_tunnels, n_tunnels):
1047                 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1048                 args2[u"sw_if_index"] = ipsec_tunnels[i]
1049                 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1050                     nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1051                     prefix_len=128 if raddr_ip2.version == 6 else 32,
1052                     interface=ipsec_tunnels[i]
1053                 )
1054                 history = bool(not 1 < i < n_tunnels - 2)
1055                 papi_exec.add(cmd1, history=history, **args1).\
1056                     add(cmd2, history=history, **args2).\
1057                     add(cmd3, history=history, **args3)
1058             papi_exec.get_replies(err_msg)
1059
1060         with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1061             if not existing_tunnels:
1062                 # Set IP address on VPP node 2 interface
1063                 cmd1 = u"sw_interface_add_del_address"
1064                 args1 = dict(
1065                     sw_if_index=InterfaceUtil.get_interface_index(
1066                         nodes[u"DUT2"], if2_key
1067                     ),
1068                     is_add=True,
1069                     del_all=False,
1070                     prefix=IPUtil.create_prefix_object(
1071                         if2_ip, 96 if if2_ip.version == 6 else 24
1072                     )
1073                 )
1074                 err_msg = f"Failed to set IP address on interface {if2_key} " \
1075                     f"on host {nodes[u'DUT2'][u'host']}"
1076                 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1077             # Configure IPsec tunnel interfaces
1078             cmd2 = u"ipsec_tunnel_if_add_del"
1079             args2 = dict(
1080                 is_add=1,
1081                 local_ip=IPUtil.create_ip_address_object(if2_ip),
1082                 remote_ip=None,
1083                 local_spi=0,
1084                 remote_spi=0,
1085                 crypto_alg=crypto_alg.alg_int_repr,
1086                 local_crypto_key_len=0,
1087                 local_crypto_key=None,
1088                 remote_crypto_key_len=0,
1089                 remote_crypto_key=None,
1090                 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1091                 local_integ_key_len=0,
1092                 local_integ_key=None,
1093                 remote_integ_key_len=0,
1094                 remote_integ_key=None,
1095                 tx_table_id=0
1096             )
1097             err_msg = f"Failed to add IPsec tunnel interfaces " \
1098                 f"on host {nodes[u'DUT2'][u'host']}"
1099             ipsec_tunnels = [None] * existing_tunnels
1100             for i in range(existing_tunnels, n_tunnels):
1101                 args2[u"local_spi"] = spi_2 + i
1102                 args2[u"remote_spi"] = spi_1 + i
1103                 args2[u"local_ip"] = IPUtil.create_ip_address_object(if2_ip)
1104                 args2[u"remote_ip"] = IPUtil.create_ip_address_object(
1105                     if1_ip + i * addr_incr)
1106                 args2[u"local_crypto_key_len"] = len(ckeys[i])
1107                 args2[u"local_crypto_key"] = ckeys[i]
1108                 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1109                 args2[u"remote_crypto_key"] = ckeys[i]
1110                 if integ_alg:
1111                     args2[u"local_integ_key_len"] = len(ikeys[i])
1112                     args2[u"local_integ_key"] = ikeys[i]
1113                     args2[u"remote_integ_key_len"] = len(ikeys[i])
1114                     args2[u"remote_integ_key"] = ikeys[i]
1115                 history = bool(not 1 < i < n_tunnels - 2)
1116                 papi_exec.add(cmd2, history=history, **args2)
1117             replies = papi_exec.get_replies(err_msg)
1118             for reply in replies:
1119                 if u"sw_if_index" in reply:
1120                     ipsec_tunnels.append(reply[u"sw_if_index"])
1121             if not existing_tunnels:
1122                 # Configure IP routes
1123                 cmd1 = u"ip_route_add_del"
1124                 route = IPUtil.compose_vpp_route_structure(
1125                     nodes[u"DUT2"], if1_ip.compressed,
1126                     prefix_len=32 if if1_ip.version == 6 else 8,
1127                     interface=if2_key,
1128                     gateway=(if2_ip - 1).compressed
1129                 )
1130                 args1 = dict(
1131                     is_add=1,
1132                     is_multipath=0,
1133                     route=route
1134                 )
1135                 papi_exec.add(cmd1, **args1)
1136             cmd1 = u"sw_interface_set_unnumbered"
1137             args1 = dict(
1138                 is_add=True,
1139                 sw_if_index=InterfaceUtil.get_interface_index(
1140                     nodes[u"DUT2"], if2_key
1141                 ),
1142                 unnumbered_sw_if_index=0
1143             )
1144             cmd2 = u"sw_interface_set_flags"
1145             args2 = dict(
1146                 sw_if_index=0,
1147                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1148             )
1149             cmd3 = u"ip_route_add_del"
1150             args3 = dict(
1151                 is_add=1,
1152                 is_multipath=0,
1153                 route=None
1154             )
1155             err_msg = f"Failed to add IP routes " \
1156                 f"on host {nodes[u'DUT2'][u'host']}"
1157             for i in range(existing_tunnels, n_tunnels):
1158                 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1159                 args2[u"sw_if_index"] = ipsec_tunnels[i]
1160                 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1161                     nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1162                     prefix_len=128 if raddr_ip1.version == 6 else 32,
1163                     interface=ipsec_tunnels[i]
1164                 )
1165                 history = bool(not 1 < i < n_tunnels - 2)
1166                 papi_exec.add(cmd1, history=history, **args1). \
1167                     add(cmd2, history=history, **args2). \
1168                     add(cmd3, history=history, **args3)
1169             papi_exec.get_replies(err_msg)
1170
1171     @staticmethod
1172     def _create_ipsec_script_files(dut, instances):
1173         """Create script files for configuring IPsec in containers
1174
1175         :param dut: DUT node on which to create the script files
1176         :param instances: number of containers on DUT node
1177         :type dut: string
1178         :type instances: int
1179         """
1180         scripts = []
1181         for cnf in range(0, instances):
1182             script_filename = (
1183                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1184             )
1185             scripts.append(open(script_filename, 'w'))
1186         return scripts
1187
1188     @staticmethod
1189     def _close_and_copy_ipsec_script_files(
1190             dut, nodes, instances, scripts):
1191         """Close created scripts and copy them to containers
1192
1193         :param dut: DUT node on which to create the script files
1194         :param nodes: VPP nodes
1195         :param instances: number of containers on DUT node
1196         :param scripts: dictionary holding the script files
1197         :type dut: string
1198         :type nodes: dict
1199         :type instances: int
1200         :type scripts: dict
1201         """
1202         for cnf in range(0, instances):
1203             scripts[cnf].close()
1204             script_filename = (
1205                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1206             )
1207             scp_node(nodes[dut], script_filename, script_filename)
1208
1209
1210     @staticmethod
1211     def vpp_ipsec_create_tunnel_interfaces_in_containers(
1212             nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
1213             crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1214             n_instances):
1215         """Create multiple IPsec tunnel interfaces between two VPP nodes.
1216
1217         :param nodes: VPP nodes to create tunnel interfaces.
1218         :param if1_ip_addr: VPP node 1 interface IP4 address.
1219         :param if2_ip_addr: VPP node 2 interface IP4 address.
1220         :param if1_key: VPP node 1 interface key from topology file.
1221         :param if2_key: VPP node 2 interface key from topology file.
1222         :param n_tunnels: Number of tunnell interfaces to create.
1223         :param crypto_alg: The encryption algorithm name.
1224         :param integ_alg: The integrity algorithm name.
1225         :param raddr_ip1: Policy selector remote IPv4 start address for the
1226             first tunnel in direction node1->node2.
1227         :param raddr_ip2: Policy selector remote IPv4 start address for the
1228             first tunnel in direction node2->node1.
1229         :param raddr_range: Mask specifying range of Policy selector Remote
1230             IPv4 addresses. Valid values are from 1 to 32.
1231         :param n_instances: Number of containers.
1232         :type nodes: dict
1233         :type if1_ip_addr: str
1234         :type if2_ip_addr: str
1235         :type if1_key: str
1236         :type if2_key: str
1237         :type n_tunnels: int
1238         :type crypto_alg: CryptoAlg
1239         :type integ_alg: IntegAlg
1240         :type raddr_ip1: string
1241         :type raddr_ip2: string
1242         :type raddr_range: int
1243         :type n_instances: int
1244         """
1245         spi_1 = 100000
1246         spi_2 = 200000
1247         addr_incr = 1 << (32 - raddr_range)
1248
1249         dut1_scripts = IPsecUtil._create_ipsec_script_files(
1250             u"DUT1", n_instances)
1251         dut2_scripts = IPsecUtil._create_ipsec_script_files(
1252             u"DUT2", n_instances)
1253
1254         for cnf in range(0, n_instances):
1255             dut1_scripts[cnf].write(
1256                 u"create loopback interface\n"
1257                 u"set interface state loop0 up\n\n"
1258             )
1259             dut2_scripts[cnf].write(
1260                 f"ip route add {if1_ip_addr}/8 via "
1261                 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1262             )
1263
1264         for tnl in range(0, n_tunnels):
1265             tnl_incr = tnl * addr_incr
1266             cnf = tnl % n_instances
1267             i = tnl // n_instances
1268             ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)).hex()
1269             integ = u""
1270             if integ_alg:
1271                 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)).hex()
1272                 integ = (
1273                     f"integ-alg {integ_alg.alg_name} "
1274                     f"local-integ-key {ikey} "
1275                     f"remote-integ-key {ikey} "
1276                 )
1277
1278             # Configure tunnel end point(s) on left side
1279             dut1_scripts[cnf].write(
1280                 u"set interface ip address loop0 "
1281                 f"{ip_address(if1_ip_addr) + tnl_incr}/32\n"
1282                 f"create ipsec tunnel "
1283                 f"local-ip {ip_address(if1_ip_addr) + tnl_incr} "
1284                 f"local-spi {spi_1 + tnl} "
1285                 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1286                 f"remote-spi {spi_2 + tnl} "
1287                 f"crypto-alg {crypto_alg.alg_name} "
1288                 f"local-crypto-key {ckey} "
1289                 f"remote-crypto-key {ckey} "
1290                 f"instance {i} "
1291                 f"salt 0x0 "
1292                 f"{integ} \n"
1293                 f"set interface unnumbered ipip{i} use loop0\n"
1294                 f"set interface state ipip{i} up\n"
1295                 f"ip route add {ip_address(raddr_ip2)+tnl}/32 via ipip{i}\n\n"
1296             )
1297
1298             # Configure tunnel end point(s) on right side
1299             dut2_scripts[cnf].write(
1300                 f"set ip neighbor memif1/{cnf + 1} "
1301                 f"{ip_address(if1_ip_addr) + tnl_incr} "
1302                 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1303                 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1304                 f"local-spi {spi_2 + tnl} "
1305                 f"remote-ip {ip_address(if1_ip_addr) + tnl_incr} "
1306                 f"remote-spi {spi_1 + tnl} "
1307                 f"crypto-alg {crypto_alg.alg_name} "
1308                 f"local-crypto-key {ckey} "
1309                 f"remote-crypto-key {ckey} "
1310                 f"instance {i} "
1311                 f"salt 0x0 "
1312                 f"{integ}\n"
1313                 f"set interface unnumbered ipip{i} use memif1/{cnf + 1}\n"
1314                 f"set interface state ipip{i} up\n"
1315                 f"ip route add {ip_address(raddr_ip1) + tnl}/32 via ipip{i}\n\n"
1316             )
1317
1318         IPsecUtil._close_and_copy_ipsec_script_files(
1319             u"DUT1", nodes, n_instances, dut1_scripts)
1320         IPsecUtil._close_and_copy_ipsec_script_files(
1321             u"DUT2", nodes, n_instances, dut2_scripts)
1322
1323     @staticmethod
1324     def vpp_ipsec_add_multiple_tunnels(
1325             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1326             tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1327         """Create multiple IPsec tunnels between two VPP nodes.
1328
1329         :param nodes: VPP nodes to create tunnels.
1330         :param interface1: Interface name or sw_if_index on node 1.
1331         :param interface2: Interface name or sw_if_index on node 2.
1332         :param n_tunnels: Number of tunnels to create.
1333         :param crypto_alg: The encryption algorithm name.
1334         :param integ_alg: The integrity algorithm name.
1335         :param tunnel_ip1: Tunnel node1 IPv4 address.
1336         :param tunnel_ip2: Tunnel node2 IPv4 address.
1337         :param raddr_ip1: Policy selector remote IPv4 start address for the
1338             first tunnel in direction node1->node2.
1339         :param raddr_ip2: Policy selector remote IPv4 start address for the
1340             first tunnel in direction node2->node1.
1341         :param raddr_range: Mask specifying range of Policy selector Remote
1342             IPv4 addresses. Valid values are from 1 to 32.
1343         :type nodes: dict
1344         :type interface1: str or int
1345         :type interface2: str or int
1346         :type n_tunnels: int
1347         :type crypto_alg: CryptoAlg
1348         :type integ_alg: IntegAlg
1349         :type tunnel_ip1: str
1350         :type tunnel_ip2: str
1351         :type raddr_ip1: string
1352         :type raddr_ip2: string
1353         :type raddr_range: int
1354         """
1355         spd_id = 1
1356         p_hi = 100
1357         p_lo = 10
1358         sa_id_1 = 100000
1359         sa_id_2 = 200000
1360         spi_1 = 300000
1361         spi_2 = 400000
1362
1363         crypto_key = gen_key(
1364             IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1365         ).decode()
1366         integ_key = gen_key(
1367             IPsecUtil.get_integ_alg_key_len(integ_alg)
1368         ).decode() if integ_alg else u""
1369
1370         IPsecUtil.vpp_ipsec_set_ip_route(
1371             nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1372             interface1, raddr_range)
1373         IPsecUtil.vpp_ipsec_set_ip_route(
1374             nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1375             interface2, raddr_range)
1376
1377         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1378         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1379         IPsecUtil.vpp_ipsec_policy_add(
1380             nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1381             proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1382         )
1383         IPsecUtil.vpp_ipsec_policy_add(
1384             nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1385             proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1386         )
1387
1388         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1389         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1390         IPsecUtil.vpp_ipsec_policy_add(
1391             nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1392             proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1393         )
1394         IPsecUtil.vpp_ipsec_policy_add(
1395             nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1396             proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1397         )
1398
1399         IPsecUtil.vpp_ipsec_add_sad_entries(
1400             nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1401             integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1402         )
1403         IPsecUtil.vpp_ipsec_spd_add_entries(
1404             nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1405         )
1406
1407         IPsecUtil.vpp_ipsec_add_sad_entries(
1408             nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1409             integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1410         )
1411         IPsecUtil.vpp_ipsec_spd_add_entries(
1412             nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1413         )
1414
1415         IPsecUtil.vpp_ipsec_add_sad_entries(
1416             nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1417             integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1418         )
1419
1420         IPsecUtil.vpp_ipsec_spd_add_entries(
1421             nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1422         )
1423
1424         IPsecUtil.vpp_ipsec_add_sad_entries(
1425             nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1426             integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1427         )
1428
1429         IPsecUtil.vpp_ipsec_spd_add_entries(
1430             nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1431         )
1432
1433     @staticmethod
1434     def vpp_ipsec_show(node):
1435         """Run "show ipsec" debug CLI command.
1436
1437         :param node: Node to run command on.
1438         :type node: dict
1439         """
1440         PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")