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