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