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