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