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