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