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