FIX: Pylint reduce
[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         :param dst_mac: The MAC address of destination tunnels.
569         :type node: dict
570         :type n_tunnels: int
571         :type tunnel_src: str
572         :type traffic_addr: str
573         :type tunnel_dst: str
574         :type interface: str
575         :type raddr_range: int
576         :type dst_mac: str
577         """
578         tunnel_src = ip_address(tunnel_src)
579         tunnel_dst = ip_address(tunnel_dst)
580         traffic_addr = ip_address(traffic_addr)
581         addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
582             else 1 << (32 - raddr_range)
583
584         if int(n_tunnels) > 10:
585             tmp_filename = u"/tmp/ipsec_set_ip.script"
586
587             with open(tmp_filename, 'w') as tmp_file:
588                 if_name = Topology.get_interface_name(node, interface)
589                 for i in range(n_tunnels):
590                     conf = f"exec set interface ip address {if_name} " \
591                         f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
592                         f"exec ip route add {traffic_addr + i}/" \
593                         f"{128 if traffic_addr.version == 6 else 32} " \
594                         f"via {tunnel_dst + i * addr_incr} {if_name}\n"
595                     if dst_mac:
596                         conf = f"{conf}exec set ip neighbor {if_name} " \
597                                f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
598                     tmp_file.write(conf)
599
600             VatExecutor().execute_script(
601                 tmp_filename, node, timeout=300, json_out=False,
602                 copy_on_execute=True
603             )
604             os.remove(tmp_filename)
605             return
606
607         cmd1 = u"sw_interface_add_del_address"
608         args1 = dict(
609             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
610             is_add=True,
611             del_all=False,
612             prefix=None
613         )
614         cmd2 = u"ip_route_add_del"
615         args2 = dict(
616             is_add=1,
617             is_multipath=0,
618             route=None
619         )
620         cmd3 = u"ip_neighbor_add_del"
621         args3 = dict(
622             is_add=True,
623             neighbor=dict(
624                 sw_if_index=Topology.get_interface_sw_index(node, interface),
625                 flags=0,
626                 mac_address=str(dst_mac),
627                 ip_address=None
628             )
629         )
630         err_msg = f"Failed to configure IP addresses, IP routes and " \
631             f"IP neighbor on interface {interface} on host {node[u'host']}" \
632             if dst_mac \
633             else f"Failed to configure IP addresses and IP routes " \
634                  f"on interface {interface} on host {node[u'host']}"
635
636         with PapiSocketExecutor(node) as papi_exec:
637             for i in range(n_tunnels):
638                 args1[u"prefix"] = IPUtil.create_prefix_object(
639                     tunnel_src + i * addr_incr, raddr_range
640                 )
641                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
642                     node, traffic_addr + i,
643                     prefix_len=128 if traffic_addr.version == 6 else 32,
644                     interface=interface, gateway=tunnel_dst + i * addr_incr
645                 )
646                 history = bool(not 1 < i < n_tunnels - 2)
647                 papi_exec.add(cmd1, history=history, **args1).\
648                     add(cmd2, history=history, **args2)
649                 if dst_mac:
650                     args3[u"neighbor"][u"ip_address"] = ip_address(
651                         tunnel_dst + i * addr_incr
652                     )
653                     papi_exec.add(cmd3, history=history, **args3)
654             papi_exec.get_replies(err_msg)
655
656     @staticmethod
657     def vpp_ipsec_add_spd(node, spd_id):
658         """Create Security Policy Database on the VPP node.
659
660         :param node: VPP node to add SPD on.
661         :param spd_id: SPD ID.
662         :type node: dict
663         :type spd_id: int
664         """
665         cmd = u"ipsec_spd_add_del"
666         err_msg = f"Failed to add Security Policy Database " \
667             f"on host {node[u'host']}"
668         args = dict(
669             is_add=True,
670             spd_id=int(spd_id)
671         )
672         with PapiSocketExecutor(node) as papi_exec:
673             papi_exec.add(cmd, **args).get_reply(err_msg)
674
675     @staticmethod
676     def vpp_ipsec_spd_add_if(node, spd_id, interface):
677         """Add interface to the Security Policy Database.
678
679         :param node: VPP node.
680         :param spd_id: SPD ID to add interface on.
681         :param interface: Interface name or sw_if_index.
682         :type node: dict
683         :type spd_id: int
684         :type interface: str or int
685         """
686         cmd = u"ipsec_interface_add_del_spd"
687         err_msg = f"Failed to add interface {interface} to Security Policy " \
688             f"Database {spd_id} on host {node[u'host']}"
689         args = dict(
690             is_add=True,
691             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
692             spd_id=int(spd_id)
693         )
694         with PapiSocketExecutor(node) as papi_exec:
695             papi_exec.add(cmd, **args).get_reply(err_msg)
696
697     @staticmethod
698     def vpp_ipsec_policy_add(
699             node, spd_id, priority, action, inbound=True, sa_id=None,
700             laddr_range=None, raddr_range=None, proto=None, lport_range=None,
701             rport_range=None, is_ipv6=False):
702         """Create Security Policy Database entry on the VPP node.
703
704         :param node: VPP node to add SPD entry on.
705         :param spd_id: SPD ID to add entry on.
706         :param priority: SPD entry priority, higher number = higher priority.
707         :param action: Policy action.
708         :param inbound: If True policy is for inbound traffic, otherwise
709             outbound.
710         :param sa_id: SAD entry ID for protect action.
711         :param laddr_range: Policy selector local IPv4 or IPv6 address range in
712             format IP/prefix or IP/mask. If no mask is provided,
713             it's considered to be /32.
714         :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
715             format IP/prefix or IP/mask. If no mask is provided,
716             it's considered to be /32.
717         :param proto: Policy selector next layer protocol number.
718         :param lport_range: Policy selector local TCP/UDP port range in format
719             <port_start>-<port_end>.
720         :param rport_range: Policy selector remote TCP/UDP port range in format
721             <port_start>-<port_end>.
722         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
723             not defined so it will default to address ::/0, otherwise False.
724         :type node: dict
725         :type spd_id: int
726         :type priority: int
727         :type action: PolicyAction
728         :type inbound: bool
729         :type sa_id: int
730         :type laddr_range: string
731         :type raddr_range: string
732         :type proto: int
733         :type lport_range: string
734         :type rport_range: string
735         :type is_ipv6: bool
736         """
737         if laddr_range is None:
738             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
739
740         if raddr_range is None:
741             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
742
743         cmd = u"ipsec_spd_entry_add_del"
744         err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
745             f"on host {node[u'host']}"
746
747         spd_entry = dict(
748             spd_id=int(spd_id),
749             priority=int(priority),
750             is_outbound=not inbound,
751             sa_id=int(sa_id) if sa_id else 0,
752             policy=action.policy_int_repr,
753             protocol=int(proto) if proto else 0,
754             remote_address_start=IPAddress.create_ip_address_object(
755                 ip_network(raddr_range, strict=False).network_address
756             ),
757             remote_address_stop=IPAddress.create_ip_address_object(
758                 ip_network(raddr_range, strict=False).broadcast_address
759             ),
760             local_address_start=IPAddress.create_ip_address_object(
761                 ip_network(laddr_range, strict=False).network_address
762             ),
763             local_address_stop=IPAddress.create_ip_address_object(
764                 ip_network(laddr_range, strict=False).broadcast_address
765             ),
766             remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
767             else 0,
768             remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
769             else 65535,
770             local_port_start=int(lport_range.split(u"-")[0]) if lport_range
771             else 0,
772             local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
773             else 65535
774         )
775         args = dict(
776             is_add=True,
777             entry=spd_entry
778         )
779         with PapiSocketExecutor(node) as papi_exec:
780             papi_exec.add(cmd, **args).get_reply(err_msg)
781
782     @staticmethod
783     def vpp_ipsec_spd_add_entries(
784             node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
785             raddr_range=0):
786         """Create multiple Security Policy Database entries on the VPP node.
787
788         :param node: VPP node to add SPD entries on.
789         :param n_entries: Number of SPD entries to be added.
790         :param spd_id: SPD ID to add entries on.
791         :param priority: SPD entries priority, higher number = higher priority.
792         :param inbound: If True policy is for inbound traffic, otherwise
793             outbound.
794         :param sa_id: SAD entry ID for first entry. Each subsequent entry will
795             SAD entry ID incremented by 1.
796         :param raddr_ip: Policy selector remote IPv4 start address for the first
797             entry. Remote IPv4 end address will be calculated depending on
798             raddr_range parameter. Each subsequent entry will have start address
799             next after IPv4 end address of previous entry.
800         :param raddr_range: Required IP addres range.
801         :type node: dict
802         :type n_entries: int
803         :type spd_id: int
804         :type priority: int
805         :type inbound: bool
806         :type sa_id: int
807         :type raddr_ip: str
808         :type raddr_range: int
809         """
810         raddr_ip = ip_address(raddr_ip)
811         if int(n_entries) > 10:
812             tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
813
814             with open(tmp_filename, 'w') as tmp_file:
815                 for i in range(n_entries):
816                     direction = u'inbound' if inbound else u'outbound'
817                     tunnel = f"exec ipsec policy add spd {spd_id} " \
818                         f"priority {priority} {direction} " \
819                         f"action protect sa {sa_id+i} " \
820                         f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
821                         f"- {raddr_ip + (i  + 1) * raddr_range + i} " \
822                         f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
823                     tmp_file.write(tunnel)
824             VatExecutor().execute_script(
825                 tmp_filename, node, timeout=300, json_out=False,
826                 copy_on_execute=True
827             )
828             os.remove(tmp_filename)
829             return
830
831         laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
832
833         cmd = u"ipsec_spd_entry_add_del"
834         err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
835             f"on host {node[u'host']}"
836
837         spd_entry = dict(
838             spd_id=int(spd_id),
839             priority=int(priority),
840             is_outbound=not inbound,
841             sa_id=int(sa_id) if sa_id else 0,
842             policy=getattr(PolicyAction.PROTECT, u"policy_int_repr"),
843             protocol=0,
844             remote_address_start=IPAddress.create_ip_address_object(raddr_ip),
845             remote_address_stop=IPAddress.create_ip_address_object(raddr_ip),
846             local_address_start=IPAddress.create_ip_address_object(
847                 ip_network(laddr_range, strict=False).network_address
848             ),
849             local_address_stop=IPAddress.create_ip_address_object(
850                 ip_network(laddr_range, strict=False).broadcast_address
851             ),
852             remote_port_start=0,
853             remote_port_stop=65535,
854             local_port_start=0,
855             local_port_stop=65535
856         )
857         args = dict(
858             is_add=True,
859             entry=spd_entry
860         )
861
862         with PapiSocketExecutor(node) as papi_exec:
863             for i in range(n_entries):
864                 args[u"entry"][u"remote_address_start"][u"un"] = \
865                     IPAddress.union_addr(raddr_ip + i)
866                 args[u"entry"][u"remote_address_stop"][u"un"] = \
867                     IPAddress.union_addr(raddr_ip + i)
868                 history = bool(not 1 < i < n_entries - 2)
869                 papi_exec.add(cmd, history=history, **args)
870             papi_exec.get_replies(err_msg)
871
872     @staticmethod
873     def _ipsec_create_tunnel_interfaces_dut1_vat(
874             nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
875             raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
876         """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
877
878         Generate random keys and return them (so DUT2 or TG can decrypt).
879
880         :param nodes: VPP nodes to create tunnel interfaces.
881         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
882             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
883             IPv4/IPv6 address (ip2).
884         :param if1_key: VPP node 1 interface key from topology file.
885         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
886             interface key from topology file.
887         :param n_tunnels: Number of tunnel interfaces to be there at the end.
888         :param crypto_alg: The encryption algorithm name.
889         :param integ_alg: The integrity algorithm name.
890         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
891             first tunnel in direction node2->node1.
892         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
893         :param addr_incr: IP / IPv6 address incremental step.
894         :param existing_tunnels: Number of tunnel interfaces before creation.
895             Useful mainly for reconf tests. Default 0.
896         :type nodes: dict
897         :type tun_ips: dict
898         :type if1_key: str
899         :type if2_key: str
900         :type n_tunnels: int
901         :type crypto_alg: CryptoAlg
902         :type integ_alg: Optional[IntegAlg]
903         :type raddr_ip2: IPv4Address or IPv6Address
904         :type addr_incr: int
905         :type spi_d: dict
906         :type existing_tunnels: int
907         :returns: Generated ckeys and ikeys.
908         :rtype: List[bytes], List[bytes]
909         """
910         tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
911         if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
912
913         ckeys = [bytes()] * existing_tunnels
914         ikeys = [bytes()] * existing_tunnels
915
916         vat = VatExecutor()
917         with open(tmp_fn1, u"w") as tmp_f1:
918             rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
919                 if u"DUT2" in nodes.keys() \
920                 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
921             if not existing_tunnels:
922                 tmp_f1.write(
923                     f"exec create loopback interface\n"
924                     f"exec set interface state loop0 up\n"
925                     f"exec set interface ip address {if1_n} "
926                     f"{tun_ips[u'ip2'] - 1}/"
927                     f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
928                     f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
929                     f"static\n"
930                 )
931             for i in range(existing_tunnels, n_tunnels):
932                 ckeys.append(
933                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
934                 )
935                 ikeys.append(
936                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
937                 )
938                 if integ_alg:
939                     integ = f"integ-alg {integ_alg.alg_name} " \
940                         f"integ-key {ikeys[i].hex()} "
941                 else:
942                     integ = u""
943                 tmp_f1.write(
944                     f"exec set interface ip address loop0 "
945                     f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
946                     f"exec create ipip tunnel "
947                     f"src {tun_ips[u'ip1'] + i * addr_incr} "
948                     f"dst {tun_ips[u'ip2']} "
949                     f"p2p\n"
950                     f"exec ipsec sa add {i} "
951                     f"spi {spi_d[u'spi_1'] + i} "
952                     f"crypto-alg {crypto_alg.alg_name} "
953                     f"crypto-key {ckeys[i].hex()} "
954                     f"{integ}"
955                     f"esp\n"
956                     f"exec ipsec sa add {100000 + i} "
957                     f"spi {spi_d[u'spi_2'] + i} "
958                     f"crypto-alg {crypto_alg.alg_name} "
959                     f"crypto-key {ckeys[i].hex()} "
960                     f"{integ}"
961                     f"esp\n"
962                     f"exec ipsec tunnel protect ipip{i} "
963                     f"sa-out {i} "
964                     f"sa-in {100000 + i} "
965                     f"add\n"
966                 )
967         vat.execute_script(
968             tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
969             copy_on_execute=True,
970             history=bool(n_tunnels < 100)
971         )
972         os.remove(tmp_fn1)
973
974         with open(tmp_fn1, 'w') as tmp_f1:
975             for i in range(existing_tunnels, n_tunnels):
976                 tmp_f1.write(
977                     f"exec set interface unnumbered ipip{i} use {if1_n}\n"
978                     f"exec set interface state ipip{i} up\n"
979                     f"exec ip route add "
980                     f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
981                     f"via ipip{i}\n"
982                 )
983         vat.execute_script(
984             tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
985             copy_on_execute=True,
986             history=bool(n_tunnels < 100)
987         )
988         os.remove(tmp_fn1)
989
990         return ckeys, ikeys
991
992     @staticmethod
993     def _ipsec_create_tunnel_interfaces_dut2_vat(
994             nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
995             ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
996         """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
997
998         This method accesses keys generated by DUT1 method
999         and does not return anything.
1000
1001         :param nodes: VPP nodes to create tunnel interfaces.
1002         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1003             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1004             IPv4/IPv6 address (ip2).
1005         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1006             interface key from topology file.
1007         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1008         :param crypto_alg: The encryption algorithm name.
1009         :param ckeys: List of encryption keys.
1010         :param integ_alg: The integrity algorithm name.
1011         :param ikeys: List of integrity keys.
1012         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1013         :param addr_incr: IP / IPv6 address incremental step.
1014         :param existing_tunnels: Number of tunnel interfaces before creation.
1015             Useful mainly for reconf tests. Default 0.
1016         :type nodes: dict
1017         :type tun_ips: dict
1018         :type if2_key: str
1019         :type n_tunnels: int
1020         :type crypto_alg: CryptoAlg
1021         :type ckeys: Sequence[bytes]
1022         :type integ_alg: Optional[IntegAlg]
1023         :type ikeys: Sequence[bytes]
1024         :type addr_incr: int
1025         :type spi_d: dict
1026         :type existing_tunnels: int
1027         """
1028         tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1029         if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1030
1031         vat = VatExecutor()
1032         with open(tmp_fn2, 'w') as tmp_f2:
1033             if not existing_tunnels:
1034                 tmp_f2.write(
1035                     f"exec set interface ip address {if2_n}"
1036                     f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1037                 )
1038             for i in range(existing_tunnels, n_tunnels):
1039                 if integ_alg:
1040                     integ = f"integ-alg {integ_alg.alg_name} " \
1041                         f"integ-key {ikeys[i].hex()} "
1042                 else:
1043                     integ = u""
1044                 tmp_f2.write(
1045                     f"exec create ipip tunnel "
1046                     f"src {tun_ips[u'ip2']} "
1047                     f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1048                     f"p2p\n"
1049                     f"exec ipsec sa add {100000 + i} "
1050                     f"spi {spi_d[u'spi_2'] + i} "
1051                     f"crypto-alg {crypto_alg.alg_name} "
1052                     f"crypto-key {ckeys[i].hex()} "
1053                     f"{integ}"
1054                     f"esp\n"
1055                     f"exec ipsec sa add {i} "
1056                     f"spi {spi_d[u'spi_1'] + i} "
1057                     f"crypto-alg {crypto_alg.alg_name} "
1058                     f"crypto-key {ckeys[i].hex()} "
1059                     f"{integ}"
1060                     f"esp\n"
1061                     f"exec ipsec tunnel protect ipip{i} "
1062                     f"sa-out {100000 + i} "
1063                     f"sa-in {i} "
1064                     f"add\n"
1065                 )
1066         vat.execute_script(
1067             tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1068             copy_on_execute=True,
1069             history=bool(n_tunnels < 100)
1070         )
1071         os.remove(tmp_fn2)
1072
1073         with open(tmp_fn2, 'w') as tmp_f2:
1074             if not existing_tunnels:
1075                 tmp_f2.write(
1076                     f"exec ip route add {tun_ips[u'ip1']}/8 "
1077                     f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1078                 )
1079             for i in range(existing_tunnels, n_tunnels):
1080                 tmp_f2.write(
1081                     f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1082                     f"exec set interface state ipip{i} up\n"
1083                     f"exec ip route add "
1084                     f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1085                     f"via ipip{i}\n"
1086                 )
1087         vat.execute_script(
1088             tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1089             copy_on_execute=True,
1090             history=bool(n_tunnels < 100)
1091         )
1092         os.remove(tmp_fn2)
1093
1094     @staticmethod
1095     def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1096         """Create loopback interface and set IP address on VPP node 1 interface
1097         using PAPI.
1098
1099         :param nodes: VPP nodes to create tunnel interfaces.
1100         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1101             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1102             IPv4/IPv6 address (ip2).
1103         :param if1_key: VPP node 1 interface key from topology file.
1104         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1105             interface key from topology file.
1106         :type nodes: dict
1107         :type tun_ips: dict
1108         :type if1_key: str
1109         :type if2_key: str
1110         """
1111         with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1112             # Create loopback interface on DUT1, set it to up state
1113             cmd = u"create_loopback_instance"
1114             args = dict(
1115                 mac_address=0,
1116                 is_specified=False,
1117                 user_instance=0,
1118             )
1119             err_msg = f"Failed to create loopback interface " \
1120                 f"on host {nodes[u'DUT1'][u'host']}"
1121             loop_sw_if_idx = papi_exec.add(cmd, **args). \
1122                 get_sw_if_index(err_msg)
1123             cmd = u"sw_interface_set_flags"
1124             args = dict(
1125                 sw_if_index=loop_sw_if_idx,
1126                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1127             )
1128             err_msg = f"Failed to set loopback interface state up " \
1129                 f"on host {nodes[u'DUT1'][u'host']}"
1130             papi_exec.add(cmd, **args).get_reply(err_msg)
1131             # Set IP address on VPP node 1 interface
1132             cmd = u"sw_interface_add_del_address"
1133             args = dict(
1134                 sw_if_index=InterfaceUtil.get_interface_index(
1135                     nodes[u"DUT1"], if1_key
1136                 ),
1137                 is_add=True,
1138                 del_all=False,
1139                 prefix=IPUtil.create_prefix_object(
1140                     tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1141                     else 24
1142                 )
1143             )
1144             err_msg = f"Failed to set IP address on interface {if1_key} " \
1145                 f"on host {nodes[u'DUT1'][u'host']}"
1146             papi_exec.add(cmd, **args).get_reply(err_msg)
1147             cmd2 = u"ip_neighbor_add_del"
1148             args2 = dict(
1149                 is_add=1,
1150                 neighbor=dict(
1151                     sw_if_index=Topology.get_interface_sw_index(
1152                         nodes[u"DUT1"], if1_key
1153                     ),
1154                     flags=1,
1155                     mac_address=str(
1156                         Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1157                         if u"DUT2" in nodes.keys()
1158                         else Topology.get_interface_mac(
1159                             nodes[u"TG"], if2_key
1160                         )
1161                     ),
1162                     ip_address=tun_ips[u"ip2"].compressed
1163                 )
1164             )
1165             err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1166             papi_exec.add(cmd2, **args2).get_reply(err_msg)
1167
1168             return loop_sw_if_idx
1169
1170     @staticmethod
1171     def _ipsec_create_tunnel_interfaces_dut1_papi(
1172             nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1173             raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1174         """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1175
1176         Generate random keys and return them (so DUT2 or TG can decrypt).
1177
1178         :param nodes: VPP nodes to create tunnel interfaces.
1179         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1180             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1181             IPv4/IPv6 address (ip2).
1182         :param if1_key: VPP node 1 interface key from topology file.
1183         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1184             interface key from topology file.
1185         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1186         :param crypto_alg: The encryption algorithm name.
1187         :param integ_alg: The integrity algorithm name.
1188         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1189             first tunnel in direction node2->node1.
1190         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1191         :param addr_incr: IP / IPv6 address incremental step.
1192         :param existing_tunnels: Number of tunnel interfaces before creation.
1193             Useful mainly for reconf tests. Default 0.
1194         :type nodes: dict
1195         :type tun_ips: dict
1196         :type if1_key: str
1197         :type if2_key: str
1198         :type n_tunnels: int
1199         :type crypto_alg: CryptoAlg
1200         :type integ_alg: Optional[IntegAlg]
1201         :type raddr_ip2: IPv4Address or IPv6Address
1202         :type addr_incr: int
1203         :type spi_d: dict
1204         :type existing_tunnels: int
1205         :returns: Generated ckeys and ikeys.
1206         :rtype: List[bytes], List[bytes]
1207         """
1208         if not existing_tunnels:
1209             loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1210                 nodes, tun_ips, if1_key, if2_key
1211             )
1212         else:
1213             loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1214                 nodes[u"DUT1"], u"loop0"
1215             )
1216         with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1217             # Configure IP addresses on loop0 interface
1218             cmd = u"sw_interface_add_del_address"
1219             args = dict(
1220                 sw_if_index=loop_sw_if_idx,
1221                 is_add=True,
1222                 del_all=False,
1223                 prefix=None
1224             )
1225             for i in range(existing_tunnels, n_tunnels):
1226                 args[u"prefix"] = IPUtil.create_prefix_object(
1227                     tun_ips[u"ip1"] + i * addr_incr,
1228                     128 if tun_ips[u"ip1"].version == 6 else 32
1229                 )
1230                 papi_exec.add(
1231                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1232                 )
1233             # Configure IPIP tunnel interfaces
1234             cmd = u"ipip_add_tunnel"
1235             ipip_tunnel = dict(
1236                 instance=Constants.BITWISE_NON_ZERO,
1237                 src=None,
1238                 dst=None,
1239                 table_id=0,
1240                 flags=int(
1241                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1242                 ),
1243                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1244                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1245             )
1246             args = dict(
1247                 tunnel=ipip_tunnel
1248             )
1249             ipip_tunnels = [None] * existing_tunnels
1250             for i in range(existing_tunnels, n_tunnels):
1251                 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1252                     tun_ips[u"ip1"] + i * addr_incr
1253                 )
1254                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1255                     tun_ips[u"ip2"]
1256                 )
1257                 papi_exec.add(
1258                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1259                 )
1260             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1261                 f" {nodes[u'DUT1'][u'host']}"
1262             ipip_tunnels.extend(
1263                 [
1264                     reply[u"sw_if_index"]
1265                     for reply in papi_exec.get_replies(err_msg)
1266                     if u"sw_if_index" in reply
1267                 ]
1268             )
1269             # Configure IPSec SAD entries
1270             ckeys = [bytes()] * existing_tunnels
1271             ikeys = [bytes()] * existing_tunnels
1272             cmd = u"ipsec_sad_entry_add_del_v2"
1273             c_key = dict(
1274                 length=0,
1275                 data=None
1276             )
1277             i_key = dict(
1278                 length=0,
1279                 data=None
1280             )
1281             sad_entry = dict(
1282                 sad_id=None,
1283                 spi=None,
1284                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1285                 crypto_algorithm=crypto_alg.alg_int_repr,
1286                 crypto_key=c_key,
1287                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1288                 integrity_key=i_key,
1289                 flags=None,
1290                 tunnel_src=0,
1291                 tunnel_dst=0,
1292                 tunnel_flags=int(
1293                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1294                 ),
1295                 dscp=int(IpDscp.IP_API_DSCP_CS0),
1296                 table_id=0,
1297                 salt=0,
1298                 udp_src_port=IPSEC_UDP_PORT_NONE,
1299                 udp_dst_port=IPSEC_UDP_PORT_NONE
1300             )
1301             args = dict(
1302                 is_add=True,
1303                 entry=sad_entry
1304             )
1305             for i in range(existing_tunnels, n_tunnels):
1306                 ckeys.append(
1307                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1308                 )
1309                 ikeys.append(
1310                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1311                 )
1312                 # SAD entry for outband / tx path
1313                 args[u"entry"][u"sad_id"] = i
1314                 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1315
1316                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1317                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1318                 if integ_alg:
1319                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1320                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1321                 args[u"entry"][u"flags"] = int(
1322                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1323                 )
1324                 papi_exec.add(
1325                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1326                 )
1327                 # SAD entry for inband / rx path
1328                 args[u"entry"][u"sad_id"] = 100000 + i
1329                 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1330
1331                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1332                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1333                 if integ_alg:
1334                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1335                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1336                 args[u"entry"][u"flags"] = int(
1337                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1338                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1339                 )
1340                 papi_exec.add(
1341                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1342                 )
1343             err_msg = f"Failed to add IPsec SAD entries on host" \
1344                 f" {nodes[u'DUT1'][u'host']}"
1345             papi_exec.get_replies(err_msg)
1346             # Add protection for tunnels with IPSEC
1347             cmd = u"ipsec_tunnel_protect_update"
1348             n_hop = dict(
1349                 address=0,
1350                 via_label=MPLS_LABEL_INVALID,
1351                 obj_id=Constants.BITWISE_NON_ZERO
1352             )
1353             ipsec_tunnel_protect = dict(
1354                 sw_if_index=None,
1355                 nh=n_hop,
1356                 sa_out=None,
1357                 n_sa_in=1,
1358                 sa_in=None
1359             )
1360             args = dict(
1361                 tunnel=ipsec_tunnel_protect
1362             )
1363             for i in range(existing_tunnels, n_tunnels):
1364                 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1365                 args[u"tunnel"][u"sa_out"] = i
1366                 args[u"tunnel"][u"sa_in"] = [100000 + i]
1367                 papi_exec.add(
1368                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1369                 )
1370             err_msg = f"Failed to add protection for tunnels with IPSEC " \
1371                 f"on host {nodes[u'DUT1'][u'host']}"
1372             papi_exec.get_replies(err_msg)
1373
1374             # Configure unnumbered interfaces
1375             cmd = u"sw_interface_set_unnumbered"
1376             args = dict(
1377                 is_add=True,
1378                 sw_if_index=InterfaceUtil.get_interface_index(
1379                     nodes[u"DUT1"], if1_key
1380                 ),
1381                 unnumbered_sw_if_index=0
1382             )
1383             for i in range(existing_tunnels, n_tunnels):
1384                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1385                 papi_exec.add(
1386                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1387                 )
1388             # Set interfaces up
1389             cmd = u"sw_interface_set_flags"
1390             args = dict(
1391                 sw_if_index=0,
1392                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1393             )
1394             for i in range(existing_tunnels, n_tunnels):
1395                 args[u"sw_if_index"] = ipip_tunnels[i]
1396                 papi_exec.add(
1397                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1398                 )
1399             # Configure IP routes
1400             cmd = u"ip_route_add_del"
1401             args = dict(
1402                 is_add=1,
1403                 is_multipath=0,
1404                 route=None
1405             )
1406             for i in range(existing_tunnels, n_tunnels):
1407                 args[u"route"] = IPUtil.compose_vpp_route_structure(
1408                     nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1409                     prefix_len=128 if raddr_ip2.version == 6 else 32,
1410                     interface=ipip_tunnels[i]
1411                 )
1412                 papi_exec.add(
1413                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1414                 )
1415             err_msg = f"Failed to add IP routes on host " \
1416                 f"{nodes[u'DUT1'][u'host']}"
1417             papi_exec.get_replies(err_msg)
1418
1419         return ckeys, ikeys
1420
1421     @staticmethod
1422     def _ipsec_create_tunnel_interfaces_dut2_papi(
1423             nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1424             ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1425         """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1426
1427         This method accesses keys generated by DUT1 method
1428         and does not return anything.
1429
1430         :param nodes: VPP nodes to create tunnel interfaces.
1431         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1432             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1433             IPv4/IPv6 address (ip2).
1434         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1435             interface key from topology file.
1436         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1437         :param crypto_alg: The encryption algorithm name.
1438         :param ckeys: List of encryption keys.
1439         :param integ_alg: The integrity algorithm name.
1440         :param ikeys: List of integrity keys.
1441         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1442         :param addr_incr: IP / IPv6 address incremental step.
1443         :param existing_tunnels: Number of tunnel interfaces before creation.
1444             Useful mainly for reconf tests. Default 0.
1445         :type nodes: dict
1446         :type tun_ips: dict
1447         :type if2_key: str
1448         :type n_tunnels: int
1449         :type crypto_alg: CryptoAlg
1450         :type ckeys: Sequence[bytes]
1451         :type integ_alg: Optional[IntegAlg]
1452         :type ikeys: Sequence[bytes]
1453         :type addr_incr: int
1454         :type spi_d: dict
1455         :type existing_tunnels: int
1456         """
1457         with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1458             if not existing_tunnels:
1459                 # Set IP address on VPP node 2 interface
1460                 cmd = u"sw_interface_add_del_address"
1461                 args = dict(
1462                     sw_if_index=InterfaceUtil.get_interface_index(
1463                         nodes[u"DUT2"], if2_key
1464                     ),
1465                     is_add=True,
1466                     del_all=False,
1467                     prefix=IPUtil.create_prefix_object(
1468                         tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1469                         else 24
1470                     )
1471                 )
1472                 err_msg = f"Failed to set IP address on interface {if2_key} " \
1473                     f"on host {nodes[u'DUT2'][u'host']}"
1474                 papi_exec.add(cmd, **args).get_reply(err_msg)
1475             # Configure IPIP tunnel interfaces
1476             cmd = u"ipip_add_tunnel"
1477             ipip_tunnel = dict(
1478                 instance=Constants.BITWISE_NON_ZERO,
1479                 src=None,
1480                 dst=None,
1481                 table_id=0,
1482                 flags=int(
1483                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1484                 ),
1485                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1486                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1487             )
1488             args = dict(
1489                 tunnel=ipip_tunnel
1490             )
1491             ipip_tunnels = [None] * existing_tunnels
1492             for i in range(existing_tunnels, n_tunnels):
1493                 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1494                     tun_ips[u"ip2"]
1495                 )
1496                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1497                     tun_ips[u"ip1"] + i * addr_incr
1498                 )
1499                 papi_exec.add(
1500                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1501                 )
1502             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1503                 f" {nodes[u'DUT2'][u'host']}"
1504             ipip_tunnels.extend(
1505                 [
1506                     reply[u"sw_if_index"]
1507                     for reply in papi_exec.get_replies(err_msg)
1508                     if u"sw_if_index" in reply
1509                 ]
1510             )
1511             # Configure IPSec SAD entries
1512             cmd = u"ipsec_sad_entry_add_del_v2"
1513             c_key = dict(
1514                 length=0,
1515                 data=None
1516             )
1517             i_key = dict(
1518                 length=0,
1519                 data=None
1520             )
1521             sad_entry = dict(
1522                 sad_id=None,
1523                 spi=None,
1524                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1525
1526                 crypto_algorithm=crypto_alg.alg_int_repr,
1527                 crypto_key=c_key,
1528                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1529                 integrity_key=i_key,
1530
1531                 flags=None,
1532                 tunnel_src=0,
1533                 tunnel_dst=0,
1534                 tunnel_flags=int(
1535                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1536                 ),
1537                 dscp=int(IpDscp.IP_API_DSCP_CS0),
1538                 table_id=0,
1539                 salt=0,
1540                 udp_src_port=IPSEC_UDP_PORT_NONE,
1541                 udp_dst_port=IPSEC_UDP_PORT_NONE
1542             )
1543             args = dict(
1544                 is_add=True,
1545                 entry=sad_entry
1546             )
1547             for i in range(existing_tunnels, n_tunnels):
1548                 ckeys.append(
1549                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1550                 )
1551                 ikeys.append(
1552                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1553                 )
1554                 # SAD entry for outband / tx path
1555                 args[u"entry"][u"sad_id"] = 100000 + i
1556                 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1557
1558                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1559                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1560                 if integ_alg:
1561                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1562                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1563                 args[u"entry"][u"flags"] = int(
1564                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1565                 )
1566                 papi_exec.add(
1567                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1568                 )
1569                 # SAD entry for inband / rx path
1570                 args[u"entry"][u"sad_id"] = i
1571                 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1572
1573                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1574                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1575                 if integ_alg:
1576                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1577                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1578                 args[u"entry"][u"flags"] = int(
1579                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1580                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1581                 )
1582                 papi_exec.add(
1583                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1584                 )
1585             err_msg = f"Failed to add IPsec SAD entries on host" \
1586                 f" {nodes[u'DUT2'][u'host']}"
1587             papi_exec.get_replies(err_msg)
1588             # Add protection for tunnels with IPSEC
1589             cmd = u"ipsec_tunnel_protect_update"
1590             n_hop = dict(
1591                 address=0,
1592                 via_label=MPLS_LABEL_INVALID,
1593                 obj_id=Constants.BITWISE_NON_ZERO
1594             )
1595             ipsec_tunnel_protect = dict(
1596                 sw_if_index=None,
1597                 nh=n_hop,
1598                 sa_out=None,
1599                 n_sa_in=1,
1600                 sa_in=None
1601             )
1602             args = dict(
1603                 tunnel=ipsec_tunnel_protect
1604             )
1605             for i in range(existing_tunnels, n_tunnels):
1606                 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1607                 args[u"tunnel"][u"sa_out"] = 100000 + i
1608                 args[u"tunnel"][u"sa_in"] = [i]
1609                 papi_exec.add(
1610                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1611                 )
1612             err_msg = f"Failed to add protection for tunnels with IPSEC " \
1613                 f"on host {nodes[u'DUT2'][u'host']}"
1614             papi_exec.get_replies(err_msg)
1615
1616             if not existing_tunnels:
1617                 # Configure IP route
1618                 cmd = u"ip_route_add_del"
1619                 route = IPUtil.compose_vpp_route_structure(
1620                     nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1621                     prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1622                     interface=if2_key,
1623                     gateway=(tun_ips[u"ip2"] - 1).compressed
1624                 )
1625                 args = dict(
1626                     is_add=1,
1627                     is_multipath=0,
1628                     route=route
1629                 )
1630                 papi_exec.add(cmd, **args)
1631             # Configure unnumbered interfaces
1632             cmd = u"sw_interface_set_unnumbered"
1633             args = dict(
1634                 is_add=True,
1635                 sw_if_index=InterfaceUtil.get_interface_index(
1636                     nodes[u"DUT2"], if2_key
1637                 ),
1638                 unnumbered_sw_if_index=0
1639             )
1640             for i in range(existing_tunnels, n_tunnels):
1641                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1642                 papi_exec.add(
1643                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1644                 )
1645             # Set interfaces up
1646             cmd = u"sw_interface_set_flags"
1647             args = dict(
1648                 sw_if_index=0,
1649                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1650             )
1651             for i in range(existing_tunnels, n_tunnels):
1652                 args[u"sw_if_index"] = ipip_tunnels[i]
1653                 papi_exec.add(
1654                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1655                 )
1656             # Configure IP routes
1657             cmd = u"ip_route_add_del"
1658             args = dict(
1659                 is_add=1,
1660                 is_multipath=0,
1661                 route=None
1662             )
1663             for i in range(existing_tunnels, n_tunnels):
1664                 args[u"route"] = IPUtil.compose_vpp_route_structure(
1665                     nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1666                     prefix_len=128 if raddr_ip1.version == 6 else 32,
1667                     interface=ipip_tunnels[i]
1668                 )
1669                 papi_exec.add(
1670                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1671                 )
1672             err_msg = f"Failed to add IP routes " \
1673                 f"on host {nodes[u'DUT2'][u'host']}"
1674             papi_exec.get_replies(err_msg)
1675
1676     @staticmethod
1677     def vpp_ipsec_create_tunnel_interfaces(
1678             nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1679             n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1680             existing_tunnels=0, return_keys=False):
1681         """Create multiple IPsec tunnel interfaces between two VPP nodes.
1682
1683         Some deployments (e.g. devicetest) need to know the generated keys.
1684         But other deployments (e.g. scale perf test) would get spammed
1685         if we returned keys every time.
1686
1687         :param nodes: VPP nodes to create tunnel interfaces.
1688         :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1689             address.
1690         :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1691             address.
1692         :param if1_key: VPP node 1 interface key from topology file.
1693         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1694             interface key from topology file.
1695         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1696         :param crypto_alg: The encryption algorithm name.
1697         :param integ_alg: The integrity algorithm name.
1698         :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1699             first tunnel in direction node1->node2.
1700         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1701             first tunnel in direction node2->node1.
1702         :param raddr_range: Mask specifying range of Policy selector Remote
1703             IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1704             and to 128 in case of IPv6.
1705         :param existing_tunnels: Number of tunnel interfaces before creation.
1706             Useful mainly for reconf tests. Default 0.
1707         :param return_keys: Whether generated keys should be returned.
1708         :type nodes: dict
1709         :type tun_if1_ip_addr: str
1710         :type tun_if2_ip_addr: str
1711         :type if1_key: str
1712         :type if2_key: str
1713         :type n_tunnels: int
1714         :type crypto_alg: CryptoAlg
1715         :type integ_alg: Optonal[IntegAlg]
1716         :type raddr_ip1: string
1717         :type raddr_ip2: string
1718         :type raddr_range: int
1719         :type existing_tunnels: int
1720         :type return_keys: bool
1721         :returns: Ckeys, ikeys, spi_1, spi_2.
1722         :rtype: Optional[List[bytes], List[bytes], int, int]
1723         """
1724         n_tunnels = int(n_tunnels)
1725         existing_tunnels = int(existing_tunnels)
1726         spi_d = dict(
1727             spi_1=100000,
1728             spi_2=200000
1729         )
1730         tun_ips = dict(
1731             ip1=ip_address(tun_if1_ip_addr),
1732             ip2=ip_address(tun_if2_ip_addr)
1733         )
1734         raddr_ip1 = ip_address(raddr_ip1)
1735         raddr_ip2 = ip_address(raddr_ip2)
1736         addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1737             else 1 << (32 - raddr_range)
1738
1739         if n_tunnels - existing_tunnels > 10:
1740             ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1741                 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1742                 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1743             )
1744             if u"DUT2" in nodes.keys():
1745                 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1746                     nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1747                     integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1748                     existing_tunnels
1749                 )
1750         else:
1751             ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1752                 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1753                 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1754             )
1755             if u"DUT2" in nodes.keys():
1756                 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1757                     nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1758                     integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1759                     existing_tunnels
1760                 )
1761
1762         if return_keys:
1763             return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1764         return None
1765
1766     @staticmethod
1767     def _create_ipsec_script_files(dut, instances):
1768         """Create script files for configuring IPsec in containers
1769
1770         :param dut: DUT node on which to create the script files
1771         :param instances: number of containers on DUT node
1772         :type dut: string
1773         :type instances: int
1774         """
1775         scripts = []
1776         for cnf in range(0, instances):
1777             script_filename = (
1778                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1779             )
1780             scripts.append(open(script_filename, 'w'))
1781         return scripts
1782
1783     @staticmethod
1784     def _close_and_copy_ipsec_script_files(
1785             dut, nodes, instances, scripts):
1786         """Close created scripts and copy them to containers
1787
1788         :param dut: DUT node on which to create the script files
1789         :param nodes: VPP nodes
1790         :param instances: number of containers on DUT node
1791         :param scripts: dictionary holding the script files
1792         :type dut: string
1793         :type nodes: dict
1794         :type instances: int
1795         :type scripts: dict
1796         """
1797         for cnf in range(0, instances):
1798             scripts[cnf].close()
1799             script_filename = (
1800                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1801             )
1802             scp_node(nodes[dut], script_filename, script_filename)
1803
1804
1805     @staticmethod
1806     def vpp_ipsec_create_tunnel_interfaces_in_containers(
1807             nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1808             raddr_ip1, raddr_ip2, raddr_range, n_instances):
1809         """Create multiple IPsec tunnel interfaces between two VPP nodes.
1810
1811         :param nodes: VPP nodes to create tunnel interfaces.
1812         :param if1_ip_addr: VPP node 1 interface IP4 address.
1813         :param if2_ip_addr: VPP node 2 interface IP4 address.
1814         :param n_tunnels: Number of tunnell interfaces to create.
1815         :param crypto_alg: The encryption algorithm name.
1816         :param integ_alg: The integrity algorithm name.
1817         :param raddr_ip1: Policy selector remote IPv4 start address for the
1818             first tunnel in direction node1->node2.
1819         :param raddr_ip2: Policy selector remote IPv4 start address for the
1820             first tunnel in direction node2->node1.
1821         :param raddr_range: Mask specifying range of Policy selector Remote
1822             IPv4 addresses. Valid values are from 1 to 32.
1823         :param n_instances: Number of containers.
1824         :type nodes: dict
1825         :type if1_ip_addr: str
1826         :type if2_ip_addr: str
1827         :type n_tunnels: int
1828         :type crypto_alg: CryptoAlg
1829         :type integ_alg: Optional[IntegAlg]
1830         :type raddr_ip1: string
1831         :type raddr_ip2: string
1832         :type raddr_range: int
1833         :type n_instances: int
1834         """
1835         spi_1 = 100000
1836         spi_2 = 200000
1837         addr_incr = 1 << (32 - raddr_range)
1838
1839         dut1_scripts = IPsecUtil._create_ipsec_script_files(
1840             u"DUT1", n_instances
1841         )
1842         dut2_scripts = IPsecUtil._create_ipsec_script_files(
1843             u"DUT2", n_instances
1844         )
1845
1846         for cnf in range(0, n_instances):
1847             dut1_scripts[cnf].write(
1848                 u"create loopback interface\n"
1849                 u"set interface state loop0 up\n\n"
1850             )
1851             dut2_scripts[cnf].write(
1852                 f"ip route add {if1_ip_addr}/8 via "
1853                 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1854             )
1855
1856         for tnl in range(0, n_tunnels):
1857             cnf = tnl % n_instances
1858             ckey = getattr(
1859                 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1860             )
1861             integ = u""
1862             ikey = getattr(
1863                 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1864             )
1865             if integ_alg:
1866                 integ = (
1867                     f"integ-alg {integ_alg.alg_name} "
1868                     f"local-integ-key {ikey} "
1869                     f"remote-integ-key {ikey} "
1870                 )
1871             # Configure tunnel end point(s) on left side
1872             dut1_scripts[cnf].write(
1873                 u"set interface ip address loop0 "
1874                 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1875                 f"create ipsec tunnel "
1876                 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1877                 f"local-spi {spi_1 + tnl} "
1878                 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1879                 f"remote-spi {spi_2 + tnl} "
1880                 f"crypto-alg {crypto_alg.alg_name} "
1881                 f"local-crypto-key {ckey} "
1882                 f"remote-crypto-key {ckey} "
1883                 f"instance {tnl // n_instances} "
1884                 f"salt 0x0 "
1885                 f"{integ} \n"
1886                 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1887                 f"set interface state ipip{tnl // n_instances} up\n"
1888                 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1889                 f"via ipip{tnl // n_instances}\n\n"
1890             )
1891             # Configure tunnel end point(s) on right side
1892             dut2_scripts[cnf].write(
1893                 f"set ip neighbor memif1/{cnf + 1} "
1894                 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1895                 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1896                 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1897                 f"local-spi {spi_2 + tnl} "
1898                 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1899                 f"remote-spi {spi_1 + tnl} "
1900                 f"crypto-alg {crypto_alg.alg_name} "
1901                 f"local-crypto-key {ckey} "
1902                 f"remote-crypto-key {ckey} "
1903                 f"instance {tnl // n_instances} "
1904                 f"salt 0x0 "
1905                 f"{integ}\n"
1906                 f"set interface unnumbered ipip{tnl // n_instances} "
1907                 f"use memif1/{cnf + 1}\n"
1908                 f"set interface state ipip{tnl // n_instances} up\n"
1909                 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1910                 f"via ipip{tnl // n_instances}\n\n"
1911             )
1912
1913         IPsecUtil._close_and_copy_ipsec_script_files(
1914             u"DUT1", nodes, n_instances, dut1_scripts)
1915         IPsecUtil._close_and_copy_ipsec_script_files(
1916             u"DUT2", nodes, n_instances, dut2_scripts)
1917
1918     @staticmethod
1919     def vpp_ipsec_add_multiple_tunnels(
1920             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1921             tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1922         """Create multiple IPsec tunnels between two VPP nodes.
1923
1924         :param nodes: VPP nodes to create tunnels.
1925         :param interface1: Interface name or sw_if_index on node 1.
1926         :param interface2: Interface name or sw_if_index on node 2.
1927         :param n_tunnels: Number of tunnels to create.
1928         :param crypto_alg: The encryption algorithm name.
1929         :param integ_alg: The integrity algorithm name.
1930         :param tunnel_ip1: Tunnel node1 IPv4 address.
1931         :param tunnel_ip2: Tunnel node2 IPv4 address.
1932         :param raddr_ip1: Policy selector remote IPv4 start address for the
1933             first tunnel in direction node1->node2.
1934         :param raddr_ip2: Policy selector remote IPv4 start address for the
1935             first tunnel in direction node2->node1.
1936         :param raddr_range: Mask specifying range of Policy selector Remote
1937             IPv4 addresses. Valid values are from 1 to 32.
1938         :type nodes: dict
1939         :type interface1: str or int
1940         :type interface2: str or int
1941         :type n_tunnels: int
1942         :type crypto_alg: CryptoAlg
1943         :type integ_alg: Optional[IntegAlg]
1944         :type tunnel_ip1: str
1945         :type tunnel_ip2: str
1946         :type raddr_ip1: string
1947         :type raddr_ip2: string
1948         :type raddr_range: int
1949         """
1950         spd_id = 1
1951         p_hi = 100
1952         p_lo = 10
1953         sa_id_1 = 100000
1954         sa_id_2 = 200000
1955         spi_1 = 300000
1956         spi_2 = 400000
1957
1958         crypto_key = gen_key(
1959             IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1960         ).decode()
1961         integ_key = gen_key(
1962             IPsecUtil.get_integ_alg_key_len(integ_alg)
1963         ).decode() if integ_alg else u""
1964
1965         rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
1966             if u"DUT2" in nodes.keys() \
1967             else Topology.get_interface_mac(nodes[u"TG"], interface2)
1968         IPsecUtil.vpp_ipsec_set_ip_route(
1969             nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1970             interface1, raddr_range, rmac)
1971
1972         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1973         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1974         IPsecUtil.vpp_ipsec_policy_add(
1975             nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1976             proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1977         )
1978         IPsecUtil.vpp_ipsec_policy_add(
1979             nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1980             proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1981         )
1982
1983         IPsecUtil.vpp_ipsec_add_sad_entries(
1984             nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1985             integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1986         )
1987         IPsecUtil.vpp_ipsec_spd_add_entries(
1988             nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1989         )
1990
1991         IPsecUtil.vpp_ipsec_add_sad_entries(
1992             nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1993             integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1994         )
1995         IPsecUtil.vpp_ipsec_spd_add_entries(
1996             nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1997         )
1998
1999         if u"DUT2" in nodes.keys():
2000             IPsecUtil.vpp_ipsec_set_ip_route(
2001                 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2002                 interface2, raddr_range)
2003
2004             IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2005             IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2006             IPsecUtil.vpp_ipsec_policy_add(
2007                 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2008                 inbound=False, proto=50, laddr_range=u"100.0.0.0/8",
2009                 raddr_range=u"100.0.0.0/8"
2010             )
2011             IPsecUtil.vpp_ipsec_policy_add(
2012                 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2013                 inbound=True, proto=50, laddr_range=u"100.0.0.0/8",
2014                 raddr_range=u"100.0.0.0/8"
2015             )
2016
2017             IPsecUtil.vpp_ipsec_add_sad_entries(
2018                 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2019                 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2
2020             )
2021             IPsecUtil.vpp_ipsec_spd_add_entries(
2022                 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1,
2023                 raddr_ip2
2024             )
2025
2026             IPsecUtil.vpp_ipsec_add_sad_entries(
2027                 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2028                 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2029             )
2030             IPsecUtil.vpp_ipsec_spd_add_entries(
2031                 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2,
2032                 raddr_ip1
2033             )
2034
2035
2036     @staticmethod
2037     def vpp_ipsec_show(node):
2038         """Run "show ipsec" debug CLI command.
2039
2040         :param node: Node to run command on.
2041         :type node: dict
2042         """
2043         PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
2044
2045     @staticmethod
2046     def show_ipsec_security_association(node):
2047         """Show IPSec security association.
2048
2049         :param node: DUT node.
2050         :type node: dict
2051         """
2052         cmds = [
2053             u"ipsec_sa_v2_dump"
2054         ]
2055         PapiSocketExecutor.dump_and_log(node, cmds)