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