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