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