86bed716d13137e0c110bac2fd9b8304ce247eb2
[csit.git] / resources / libraries / python / IPsecUtil.py
1 # Copyright (c) 2022 Cisco and/or its affiliates.
2 # Copyright (c) 2022 PANTHEON.tech s.r.o.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """IPsec utilities library."""
16
17 import os
18
19 from enum import Enum, IntEnum
20 from io import open
21 from ipaddress import ip_network, ip_address
22 from random import choice
23 from string import ascii_letters
24
25 from 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.VatExecutor import VatExecutor
36 from resources.libraries.python.VPPUtil import VPPUtil
37 from resources.libraries.python.FlowUtil import FlowUtil
38
39
40 IPSEC_UDP_PORT_NONE = 0xffff
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         :param node: VPP node to set IPsec async mode.
321         :param async_enable: Async mode on or off.
322         :type node: dict
323         :type async_enable: int
324         :raises RuntimeError: If failed to set IPsec async mode or if no API
325             reply received.
326         """
327         cmd = u"ipsec_set_async_mode"
328         err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
329         args = dict(
330             async_enable=async_enable
331         )
332         with PapiSocketExecutor(node) as papi_exec:
333             papi_exec.add(cmd, **args).get_reply(err_msg)
334
335     @staticmethod
336     def vpp_ipsec_crypto_sw_scheduler_set_worker(
337             node, workers, crypto_enable=False):
338         """Enable or disable crypto on specific vpp worker threads.
339
340         :param node: VPP node to enable or disable crypto for worker threads.
341         :param workers: List of VPP thread numbers.
342         :param crypto_enable: Disable or enable crypto work.
343         :type node: dict
344         :type workers: Iterable[int]
345         :type crypto_enable: bool
346         :raises RuntimeError: If failed to enable or disable crypto for worker
347             thread or if no API reply received.
348         """
349         for worker in workers:
350             cmd = u"crypto_sw_scheduler_set_worker"
351             err_msg = f"Failed to disable/enable crypto for worker thread " \
352                 f"on host {node[u'host']}"
353             args = dict(
354                 worker_index=worker - 1,
355                 crypto_enable=crypto_enable
356             )
357             with PapiSocketExecutor(node) as papi_exec:
358                 papi_exec.add(cmd, **args).get_reply(err_msg)
359
360     @staticmethod
361     def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
362             nodes, workers, crypto_enable=False):
363         """Enable or disable crypto on specific vpp worker threads.
364
365         :param node: VPP node to enable or disable crypto for worker threads.
366         :param workers: List of VPP thread numbers.
367         :param crypto_enable: Disable or enable crypto work.
368         :type node: dict
369         :type workers: Iterable[int]
370         :type crypto_enable: bool
371         :raises RuntimeError: If failed to enable or disable crypto for worker
372             thread or if no API reply received.
373         """
374         for node in nodes.values():
375             if node[u"type"] == NodeType.DUT:
376                 thread_data = VPPUtil.vpp_show_threads(node)
377                 worker_cnt = len(thread_data) - 1
378                 if not worker_cnt:
379                     return None
380                 worker_ids = list()
381                 for item in thread_data:
382                     if str(item.cpu_id) in workers.split(u","):
383                         worker_ids.append(item.id)
384
385                 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
386                     node, workers=worker_ids, crypto_enable=crypto_enable
387                 )
388
389     @staticmethod
390     def vpp_ipsec_add_sad_entry(
391             node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
392             integ_key=u"", tunnel_src=None, tunnel_dst=None):
393         """Create Security Association Database entry on the VPP node.
394
395         :param node: VPP node to add SAD entry on.
396         :param sad_id: SAD entry ID.
397         :param spi: Security Parameter Index of this SAD entry.
398         :param crypto_alg: The encryption algorithm name.
399         :param crypto_key: The encryption key string.
400         :param integ_alg: The integrity algorithm name.
401         :param integ_key: The integrity key string.
402         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
403             specified ESP transport mode is used.
404         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
405             not specified ESP transport mode is used.
406         :type node: dict
407         :type sad_id: int
408         :type spi: int
409         :type crypto_alg: CryptoAlg
410         :type crypto_key: str
411         :type integ_alg: Optional[IntegAlg]
412         :type integ_key: str
413         :type tunnel_src: str
414         :type tunnel_dst: str
415         """
416         if isinstance(crypto_key, str):
417             crypto_key = crypto_key.encode(encoding=u"utf-8")
418         if isinstance(integ_key, str):
419             integ_key = integ_key.encode(encoding=u"utf-8")
420         ckey = dict(
421             length=len(crypto_key),
422             data=crypto_key
423         )
424         ikey = dict(
425             length=len(integ_key),
426             data=integ_key if integ_key else 0
427         )
428
429         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
430         if tunnel_src and tunnel_dst:
431             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
432             src_addr = ip_address(tunnel_src)
433             dst_addr = ip_address(tunnel_dst)
434             if src_addr.version == 6:
435                 flags = \
436                     flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
437         else:
438             src_addr = u""
439             dst_addr = u""
440
441         cmd = u"ipsec_sad_entry_add"
442         err_msg = f"Failed to add Security Association Database entry " \
443             f"on host {node[u'host']}"
444         sad_entry = dict(
445             sad_id=int(sad_id),
446             spi=int(spi),
447             crypto_algorithm=crypto_alg.alg_int_repr,
448             crypto_key=ckey,
449             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
450             integrity_key=ikey,
451             flags=flags,
452             tunnel=dict(
453                 src=str(src_addr),
454                 dst=str(dst_addr),
455                 table_id=0,
456                 encap_decap_flags=int(
457                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
458                 ),
459                 dscp=int(IpDscp.IP_API_DSCP_CS0),
460             ),
461             protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
462             udp_src_port=4500,  # default value in api
463             udp_dst_port=4500  # default value in api
464         )
465         args = dict(entry=sad_entry)
466         with PapiSocketExecutor(node) as papi_exec:
467             papi_exec.add(cmd, **args).get_reply(err_msg)
468
469     @staticmethod
470     def vpp_ipsec_add_sad_entries(
471             node, n_entries, sad_id, spi, crypto_alg, crypto_key,
472             integ_alg=None, integ_key=u"", tunnel_src=None,tunnel_dst=None,
473             tunnel_addr_incr=True):
474         """Create multiple Security Association Database entries on VPP node.
475
476         :param node: VPP node to add SAD entry on.
477         :param n_entries: Number of SAD entries to be created.
478         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
479             id incremented by 1.
480         :param spi: Security Parameter Index of first SAD entry. All subsequent
481             SAD entries will have spi incremented by 1.
482         :param crypto_alg: The encryption algorithm name.
483         :param crypto_key: The encryption key string.
484         :param integ_alg: The integrity algorithm name.
485         :param integ_key: The integrity key string.
486         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
487             specified ESP transport mode is used.
488         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
489             not specified ESP transport mode is used.
490         :param tunnel_addr_incr: Enable or disable tunnel IP address
491             incremental step.
492         :type node: dict
493         :type n_entries: int
494         :type sad_id: int
495         :type spi: int
496         :type crypto_alg: CryptoAlg
497         :type crypto_key: str
498         :type integ_alg: Optional[IntegAlg]
499         :type integ_key: str
500         :type tunnel_src: str
501         :type tunnel_dst: str
502         :type tunnel_addr_incr: bool
503         """
504         if isinstance(crypto_key, str):
505             crypto_key = crypto_key.encode(encoding=u"utf-8")
506         if isinstance(integ_key, str):
507             integ_key = integ_key.encode(encoding=u"utf-8")
508         if tunnel_src and tunnel_dst:
509             src_addr = ip_address(tunnel_src)
510             dst_addr = ip_address(tunnel_dst)
511         else:
512             src_addr = u""
513             dst_addr = u""
514
515         if tunnel_addr_incr:
516             addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
517                 else 1 << (32 - 24)
518         else:
519             addr_incr = 0
520
521         if int(n_entries) > 10:
522             tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
523
524             with open(tmp_filename, 'w') as tmp_file:
525                 for i in range(n_entries):
526                     integ = f"integ-alg {integ_alg.alg_name} " \
527                         f"integ-key {integ_key.hex()}" \
528                         if integ_alg else u""
529                     tunnel = f"tunnel src {src_addr + i * addr_incr} " \
530                         f"tunnel dst {dst_addr + i * addr_incr}" \
531                         if tunnel_src and tunnel_dst else u""
532                     conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
533                         f"crypto-alg {crypto_alg.alg_name} " \
534                         f"crypto-key {crypto_key.hex()} " \
535                         f"{integ} {tunnel}\n"
536                     tmp_file.write(conf)
537             vat = VatExecutor()
538             vat.execute_script(
539                 tmp_filename, node, timeout=300, json_out=False,
540                 copy_on_execute=True
541             )
542             os.remove(tmp_filename)
543             return
544
545         ckey = dict(
546             length=len(crypto_key),
547             data=crypto_key
548         )
549         ikey = dict(
550             length=len(integ_key),
551             data=integ_key if integ_key else 0
552         )
553
554         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
555         if tunnel_src and tunnel_dst:
556             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
557             if src_addr.version == 6:
558                 flags = flags | int(
559                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
560                 )
561
562         cmd = u"ipsec_sad_entry_add"
563         err_msg = f"Failed to add Security Association Database entry " \
564             f"on host {node[u'host']}"
565
566         sad_entry = dict(
567             sad_id=int(sad_id),
568             spi=int(spi),
569             crypto_algorithm=crypto_alg.alg_int_repr,
570             crypto_key=ckey,
571             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
572             integrity_key=ikey,
573             flags=flags,
574             tunnel=dict(
575                 src=str(src_addr),
576                 dst=str(dst_addr),
577                 table_id=0,
578                 encap_decap_flags=int(
579                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
580                 ),
581                 dscp=int(IpDscp.IP_API_DSCP_CS0),
582             ),
583             protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
584             udp_src_port=4500,  # default value in api
585             udp_dst_port=4500,  # default value in api
586         )
587         args = dict(entry=sad_entry)
588         with PapiSocketExecutor(node) as papi_exec:
589             for i in range(n_entries):
590                 args[u"entry"][u"sad_id"] = int(sad_id) + i
591                 args[u"entry"][u"spi"] = int(spi) + i
592                 args[u"entry"][u"tunnel"][u"src"] = (
593                     str(src_addr + i * addr_incr)
594                     if tunnel_src and tunnel_dst else src_addr
595                 )
596                 args[u"entry"][u"tunnel"][u"dst"] = (
597                     str(dst_addr + i * addr_incr)
598                     if tunnel_src and tunnel_dst else dst_addr
599                 )
600                 history = bool(not 1 < i < n_entries - 2)
601                 papi_exec.add(cmd, history=history, **args)
602             papi_exec.get_replies(err_msg)
603
604     @staticmethod
605     def vpp_ipsec_set_ip_route(
606             node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
607             raddr_range, dst_mac=None):
608         """Set IP address and route on interface.
609
610         :param node: VPP node to add config on.
611         :param n_tunnels: Number of tunnels to create.
612         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
613         :param traffic_addr: Traffic destination IP address to route.
614         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
615         :param interface: Interface key on node 1.
616         :param raddr_range: Mask specifying range of Policy selector Remote IP
617             addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
618             in case of IPv6.
619         :param dst_mac: The MAC address of destination tunnels.
620         :type node: dict
621         :type n_tunnels: int
622         :type tunnel_src: str
623         :type traffic_addr: str
624         :type tunnel_dst: str
625         :type interface: str
626         :type raddr_range: int
627         :type dst_mac: str
628         """
629         tunnel_src = ip_address(tunnel_src)
630         tunnel_dst = ip_address(tunnel_dst)
631         traffic_addr = ip_address(traffic_addr)
632         tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
633         addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
634             else 1 << (32 - raddr_range)
635
636         if int(n_tunnels) > 10:
637             tmp_filename = u"/tmp/ipsec_set_ip.script"
638
639             with open(tmp_filename, 'w') as tmp_file:
640                 if_name = Topology.get_interface_name(node, interface)
641                 for i in range(n_tunnels):
642                     tunnel_dst_addr = tunnel_dst + i * addr_incr
643                     conf = f"exec set interface ip address {if_name} " \
644                         f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
645                         f"exec ip route add {traffic_addr + i}/" \
646                         f"{tunnel_dst_prefix} " \
647                         f"via {tunnel_dst_addr} {if_name}\n" \
648                         f"exec ip route add {tunnel_dst_addr}/" \
649                         f"{tunnel_dst_prefix} " \
650                         f"via {tunnel_dst_addr} {if_name}\n"
651                     if dst_mac:
652                         conf = f"{conf}exec set ip neighbor {if_name} " \
653                                f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
654                     tmp_file.write(conf)
655
656             VatExecutor().execute_script(
657                 tmp_filename, node, timeout=300, json_out=False,
658                 copy_on_execute=True
659             )
660             os.remove(tmp_filename)
661             return
662
663         cmd1 = u"sw_interface_add_del_address"
664         args1 = dict(
665             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
666             is_add=True,
667             del_all=False,
668             prefix=None
669         )
670         cmd2 = u"ip_route_add_del"
671         args2 = dict(
672             is_add=1,
673             is_multipath=0,
674             route=None
675         )
676         cmd3 = u"ip_neighbor_add_del"
677         args3 = dict(
678             is_add=True,
679             neighbor=dict(
680                 sw_if_index=Topology.get_interface_sw_index(node, interface),
681                 flags=0,
682                 mac_address=str(dst_mac),
683                 ip_address=None
684             )
685         )
686         err_msg = f"Failed to configure IP addresses, IP routes and " \
687             f"IP neighbor on interface {interface} on host {node[u'host']}" \
688             if dst_mac \
689             else f"Failed to configure IP addresses and IP routes " \
690                  f"on interface {interface} on host {node[u'host']}"
691
692         with PapiSocketExecutor(node) as papi_exec:
693             for i in range(n_tunnels):
694                 tunnel_dst_addr = tunnel_dst + i * addr_incr
695                 args1[u"prefix"] = IPUtil.create_prefix_object(
696                     tunnel_src + i * addr_incr, raddr_range
697                 )
698                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
699                     node, traffic_addr + i,
700                     prefix_len=tunnel_dst_prefix,
701                     interface=interface, gateway=tunnel_dst_addr
702                 )
703                 history = bool(not 1 < i < n_tunnels - 2)
704                 papi_exec.add(cmd1, history=history, **args1).\
705                     add(cmd2, history=history, **args2)
706
707                 args2[u"route"] = IPUtil.compose_vpp_route_structure(
708                     node, tunnel_dst_addr,
709                     prefix_len=tunnel_dst_prefix,
710                     interface=interface, gateway=tunnel_dst_addr
711                 )
712                 papi_exec.add(cmd2, history=history, **args2)
713
714                 if dst_mac:
715                     args3[u"neighbor"][u"ip_address"] = ip_address(
716                         tunnel_dst_addr
717                     )
718                     papi_exec.add(cmd3, history=history, **args3)
719             papi_exec.get_replies(err_msg)
720
721     @staticmethod
722     def vpp_ipsec_add_spd(node, spd_id):
723         """Create Security Policy Database on the VPP node.
724
725         :param node: VPP node to add SPD on.
726         :param spd_id: SPD ID.
727         :type node: dict
728         :type spd_id: int
729         """
730         cmd = u"ipsec_spd_add_del"
731         err_msg = f"Failed to add Security Policy Database " \
732             f"on host {node[u'host']}"
733         args = dict(
734             is_add=True,
735             spd_id=int(spd_id)
736         )
737         with PapiSocketExecutor(node) as papi_exec:
738             papi_exec.add(cmd, **args).get_reply(err_msg)
739
740     @staticmethod
741     def vpp_ipsec_spd_add_if(node, spd_id, interface):
742         """Add interface to the Security Policy Database.
743
744         :param node: VPP node.
745         :param spd_id: SPD ID to add interface on.
746         :param interface: Interface name or sw_if_index.
747         :type node: dict
748         :type spd_id: int
749         :type interface: str or int
750         """
751         cmd = u"ipsec_interface_add_del_spd"
752         err_msg = f"Failed to add interface {interface} to Security Policy " \
753             f"Database {spd_id} on host {node[u'host']}"
754         args = dict(
755             is_add=True,
756             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
757             spd_id=int(spd_id)
758         )
759         with PapiSocketExecutor(node) as papi_exec:
760             papi_exec.add(cmd, **args).get_reply(err_msg)
761
762     @staticmethod
763     def vpp_ipsec_create_spds_match_nth_entry(
764             node, dir1_interface, dir2_interface, entry_amount,
765             local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
766             inbound=False, bidirectional=True):
767         """Create one matching SPD entry for inbound or outbound traffic on
768         a DUT for each traffic direction and also create entry_amount - 1
769         non-matching SPD entries. Create a Security Policy Database on each
770         outbound interface where these entries will be configured.
771         The matching SPD entry will have the lowest priority, input action and
772         will be configured to match the IP flow. The non-matching entries will
773         be the same, except with higher priority and non-matching IP flows.
774
775         Action Protect is currently not supported.
776
777         :param node: VPP node to configured the SPDs and their entries.
778         :param dir1_interface: The interface in direction 1 where the entries
779             will be checked.
780         :param dir2_interface: The interface in direction 2 where the entries
781             will be checked.
782         :param entry_amount: The number of SPD entries to configure. If
783             entry_amount == 1, no non-matching entries will be configured.
784         :param local_addr_range: Matching local address range in direction 1
785             in format IP/prefix or IP/mask. If no mask is provided, it's
786             considered to be /32.
787         :param remote_addr_range: Matching remote address range in
788             direction 1 in format IP/prefix or IP/mask. If no mask is
789             provided, it's considered to be /32.
790         :param action: Policy action.
791         :param inbound: If True policy is for inbound traffic, otherwise
792             outbound.
793         :param bidirectional: When True, will create SPDs in both directions
794             of traffic. When False, only in one direction.
795         :type node: dict
796         :type dir1_interface: Union[string, int]
797         :type dir2_interface: Union[string, int]
798         :type entry_amount: int
799         :type local_addr_range:
800             Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
801         :type remote_addr_range:
802             Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
803         :type action: IPsecUtil.PolicyAction
804         :type inbound: bool
805         :type bidirectional: bool
806         :raises NotImplementedError: When the action is PolicyAction.PROTECT.
807         """
808
809         if action == PolicyAction.PROTECT:
810             raise NotImplementedError('Policy action PROTECT is not supported.')
811
812         spd_id_dir1 = 1
813         spd_id_dir2 = 2
814         matching_priority = 1
815
816         IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
817         IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
818         # matching entry direction 1
819         IPsecUtil.vpp_ipsec_add_spd_entry(
820             node, spd_id_dir1, matching_priority, action,
821             inbound=inbound, laddr_range=local_addr_range,
822             raddr_range=remote_addr_range
823         )
824
825         if bidirectional:
826             IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
827             IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
828
829             # matching entry direction 2, the address ranges are switched
830             IPsecUtil.vpp_ipsec_add_spd_entry(
831                 node, spd_id_dir2, matching_priority, action,
832                 inbound=inbound, laddr_range=remote_addr_range,
833                 raddr_range=local_addr_range
834             )
835
836         # non-matching entries
837         no_match_entry_amount = entry_amount - 1
838         if no_match_entry_amount > 0:
839             # create a NetworkIncrement representation of the network,
840             # then skip the matching network
841             no_match_local_addr_range = NetworkIncrement(
842                 ip_network(local_addr_range)
843             )
844             next(no_match_local_addr_range)
845
846             no_match_remote_addr_range = NetworkIncrement(
847                 ip_network(remote_addr_range)
848             )
849             next(no_match_remote_addr_range)
850
851             # non-matching entries direction 1
852             IPsecUtil.vpp_ipsec_add_spd_entries(
853                 node, no_match_entry_amount, spd_id_dir1,
854                 ObjIncrement(matching_priority + 1, 1), action,
855                 inbound=inbound, laddr_range=no_match_local_addr_range,
856                 raddr_range=no_match_remote_addr_range
857             )
858
859             if bidirectional:
860                 # reset the networks so that we're using a unified config
861                 # the address ranges are switched
862                 no_match_remote_addr_range = NetworkIncrement(
863                     ip_network(local_addr_range)
864                 )
865                 next(no_match_remote_addr_range)
866
867                 no_match_local_addr_range = NetworkIncrement(
868                     ip_network(remote_addr_range)
869                 )
870                 next(no_match_local_addr_range)
871                 # non-matching entries direction 2
872                 IPsecUtil.vpp_ipsec_add_spd_entries(
873                     node, no_match_entry_amount, spd_id_dir2,
874                     ObjIncrement(matching_priority + 1, 1), action,
875                     inbound=inbound, laddr_range=no_match_local_addr_range,
876                     raddr_range=no_match_remote_addr_range
877                 )
878
879         IPsecUtil.vpp_ipsec_show_all(node)
880
881     @staticmethod
882     def vpp_ipsec_add_spd_entry(
883             node, spd_id, priority, action, inbound=True, sa_id=None,
884             proto=None, laddr_range=None, raddr_range=None, lport_range=None,
885             rport_range=None, is_ipv6=False):
886         """Create Security Policy Database entry on the VPP node.
887
888         :param node: VPP node to add SPD entry on.
889         :param spd_id: SPD ID to add entry on.
890         :param priority: SPD entry priority, higher number = higher priority.
891         :param action: Policy action.
892         :param inbound: If True policy is for inbound traffic, otherwise
893             outbound.
894         :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
895         :param proto: Policy selector next layer protocol number.
896         :param laddr_range: Policy selector local IPv4 or IPv6 address range
897             in format IP/prefix or IP/mask. If no mask is provided,
898             it's considered to be /32.
899         :param raddr_range: Policy selector remote IPv4 or IPv6 address range
900             in format IP/prefix or IP/mask. If no mask is provided,
901             it's considered to be /32.
902         :param lport_range: Policy selector local TCP/UDP port range in format
903             <port_start>-<port_end>.
904         :param rport_range: Policy selector remote TCP/UDP port range in format
905             <port_start>-<port_end>.
906         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
907             not defined so it will default to address ::/0, otherwise False.
908         :type node: dict
909         :type spd_id: int
910         :type priority: int
911         :type action: IPsecUtil.PolicyAction
912         :type inbound: bool
913         :type sa_id: int
914         :type proto: int
915         :type laddr_range: string
916         :type raddr_range: string
917         :type lport_range: string
918         :type rport_range: string
919         :type is_ipv6: bool
920         """
921         if laddr_range is None:
922             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
923
924         if raddr_range is None:
925             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
926
927         local_net = ip_network(laddr_range, strict=False)
928         remote_net = ip_network(raddr_range, strict=False)
929
930         cmd = u"ipsec_spd_entry_add_del"
931         err_msg = f"Failed to add entry to Security Policy Database " \
932                   f"{spd_id} on host {node[u'host']}"
933
934         spd_entry = dict(
935             spd_id=int(spd_id),
936             priority=int(priority),
937             is_outbound=not inbound,
938             sa_id=int(sa_id) if sa_id else 0,
939             policy=int(action),
940             protocol=int(proto) if proto else 0,
941             remote_address_start=IPAddress.create_ip_address_object(
942                 remote_net.network_address
943             ),
944             remote_address_stop=IPAddress.create_ip_address_object(
945                 remote_net.broadcast_address
946             ),
947             local_address_start=IPAddress.create_ip_address_object(
948                 local_net.network_address
949             ),
950             local_address_stop=IPAddress.create_ip_address_object(
951                 local_net.broadcast_address
952             ),
953             remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
954             else 0,
955             remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
956             else 65535,
957             local_port_start=int(lport_range.split(u"-")[0]) if lport_range
958             else 0,
959             local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
960             else 65535
961         )
962         args = dict(
963             is_add=True,
964             entry=spd_entry
965         )
966         with PapiSocketExecutor(node) as papi_exec:
967             papi_exec.add(cmd, **args).get_reply(err_msg)
968
969     @staticmethod
970     def vpp_ipsec_add_spd_entries(
971             node, n_entries, spd_id, priority, action, inbound, sa_id=None,
972             proto=None, laddr_range=None, raddr_range=None, lport_range=None,
973             rport_range=None, is_ipv6=False):
974         """Create multiple Security Policy Database entries on the VPP node.
975
976         :param node: VPP node to add SPD entries on.
977         :param n_entries: Number of SPD entries to be added.
978         :param spd_id: SPD ID to add entries on.
979         :param priority: SPD entries priority, higher number = higher priority.
980         :param action: Policy action.
981         :param inbound: If True policy is for inbound traffic, otherwise
982             outbound.
983         :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
984         :param proto: Policy selector next layer protocol number.
985         :param laddr_range: Policy selector local IPv4 or IPv6 address range
986             in format IP/prefix or IP/mask. If no mask is provided,
987             it's considered to be /32.
988         :param raddr_range: Policy selector remote IPv4 or IPv6 address range
989             in format IP/prefix or IP/mask. If no mask is provided,
990             it's considered to be /32.
991         :param lport_range: Policy selector local TCP/UDP port range in format
992             <port_start>-<port_end>.
993         :param rport_range: Policy selector remote TCP/UDP port range in format
994             <port_start>-<port_end>.
995         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
996             not defined so it will default to address ::/0, otherwise False.
997         :type node: dict
998         :type n_entries: int
999         :type spd_id: int
1000         :type priority: IPsecUtil.ObjIncrement
1001         :type action: IPsecUtil.PolicyAction
1002         :type inbound: bool
1003         :type sa_id: IPsecUtil.ObjIncrement
1004         :type proto: int
1005         :type laddr_range: IPsecUtil.NetworkIncrement
1006         :type raddr_range: IPsecUtil.NetworkIncrement
1007         :type lport_range: string
1008         :type rport_range: string
1009         :type is_ipv6: bool
1010         """
1011         if laddr_range is None:
1012             laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1013             laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1014
1015         if raddr_range is None:
1016             raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1017             raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1018
1019         lport_range_start = 0
1020         lport_range_stop = 65535
1021         if lport_range:
1022             lport_range_start, lport_range_stop = lport_range.split('-')
1023
1024         rport_range_start = 0
1025         rport_range_stop = 65535
1026         if rport_range:
1027             rport_range_start, rport_range_stop = rport_range.split('-')
1028
1029         if int(n_entries) > 10:
1030             tmp_filename = f"/tmp/ipsec_spd_{spd_id}_add_del_entry.script"
1031
1032             with open(tmp_filename, 'w') as tmp_file:
1033                 for _ in range(n_entries):
1034                     direction = u'inbound' if inbound else u'outbound'
1035                     sa = f' sa {sa_id.inc_fmt()}' if sa_id is not None else ''
1036                     protocol = f' protocol {protocol}' if proto else ''
1037                     local_port_range = f' local-port-range ' \
1038                         f'{lport_range_start} - {lport_range_stop}' \
1039                         if lport_range else ''
1040                     remote_port_range = f' remote-port-range ' \
1041                         f'{rport_range_start} - {rport_range_stop}' \
1042                         if rport_range else ''
1043
1044                     spd_cfg = f"exec ipsec policy add spd {spd_id} " \
1045                         f"priority {priority.inc_fmt()} {direction}" \
1046                         f"{protocol} action {action}{sa} " \
1047                         f"local-ip-range {laddr_range.inc_fmt()} " \
1048                         f"remote-ip-range {raddr_range.inc_fmt()}" \
1049                         f"{local_port_range}{remote_port_range}\n"
1050
1051                     tmp_file.write(spd_cfg)
1052
1053             VatExecutor().execute_script(
1054                 tmp_filename, node, timeout=300, json_out=False,
1055                 copy_on_execute=True
1056             )
1057             os.remove(tmp_filename)
1058             return
1059
1060         for _ in range(n_entries):
1061             IPsecUtil.vpp_ipsec_add_spd_entry(
1062                 node, spd_id, next(priority), action, inbound,
1063                 next(sa_id) if sa_id is not None else sa_id,
1064                 proto, next(laddr_range), next(raddr_range), lport_range,
1065                 rport_range, is_ipv6
1066             )
1067
1068     @staticmethod
1069     def _ipsec_create_tunnel_interfaces_dut1_vat(
1070             nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1071             raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1072         """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
1073
1074         Generate random keys and return them (so DUT2 or TG can decrypt).
1075
1076         :param nodes: VPP nodes to create tunnel interfaces.
1077         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1078             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1079             IPv4/IPv6 address (ip2).
1080         :param if1_key: VPP node 1 interface key from topology file.
1081         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1082             interface key from topology file.
1083         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1084         :param crypto_alg: The encryption algorithm name.
1085         :param integ_alg: The integrity algorithm name.
1086         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1087             first tunnel in direction node2->node1.
1088         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1089         :param addr_incr: IP / IPv6 address incremental step.
1090         :param existing_tunnels: Number of tunnel interfaces before creation.
1091             Useful mainly for reconf tests. Default 0.
1092         :type nodes: dict
1093         :type tun_ips: dict
1094         :type if1_key: str
1095         :type if2_key: str
1096         :type n_tunnels: int
1097         :type crypto_alg: CryptoAlg
1098         :type integ_alg: Optional[IntegAlg]
1099         :type raddr_ip2: IPv4Address or IPv6Address
1100         :type addr_incr: int
1101         :type spi_d: dict
1102         :type existing_tunnels: int
1103         :returns: Generated ckeys and ikeys.
1104         :rtype: List[bytes], List[bytes]
1105         """
1106         tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
1107         if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
1108
1109         ckeys = [bytes()] * existing_tunnels
1110         ikeys = [bytes()] * existing_tunnels
1111
1112         vat = VatExecutor()
1113         with open(tmp_fn1, u"w") as tmp_f1:
1114             rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
1115                 if u"DUT2" in nodes.keys() \
1116                 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
1117             if not existing_tunnels:
1118                 tmp_f1.write(
1119                     f"exec create loopback interface\n"
1120                     f"exec set interface state loop0 up\n"
1121                     f"exec set interface ip address {if1_n} "
1122                     f"{tun_ips[u'ip2'] - 1}/"
1123                     f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
1124                     f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
1125                     f"static\n"
1126                 )
1127             for i in range(existing_tunnels, n_tunnels):
1128                 ckeys.append(
1129                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1130                 )
1131                 ikeys.append(
1132                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1133                 )
1134                 if integ_alg:
1135                     integ = f"integ-alg {integ_alg.alg_name} " \
1136                         f"integ-key {ikeys[i].hex()} "
1137                 else:
1138                     integ = u""
1139                 tmp_f1.write(
1140                     f"exec set interface ip address loop0 "
1141                     f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
1142                     f"exec create ipip tunnel "
1143                     f"src {tun_ips[u'ip1'] + i * addr_incr} "
1144                     f"dst {tun_ips[u'ip2']} "
1145                     f"p2p\n"
1146                     f"exec ipsec sa add {i} "
1147                     f"spi {spi_d[u'spi_1'] + i} "
1148                     f"crypto-alg {crypto_alg.alg_name} "
1149                     f"crypto-key {ckeys[i].hex()} "
1150                     f"{integ}"
1151                     f"esp\n"
1152                     f"exec ipsec sa add {100000 + i} "
1153                     f"spi {spi_d[u'spi_2'] + i} "
1154                     f"crypto-alg {crypto_alg.alg_name} "
1155                     f"crypto-key {ckeys[i].hex()} "
1156                     f"{integ}"
1157                     f"esp\n"
1158                     f"exec ipsec tunnel protect ipip{i} "
1159                     f"sa-out {i} "
1160                     f"sa-in {100000 + i} "
1161                     f"add\n"
1162                 )
1163         vat.execute_script(
1164             tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1165             copy_on_execute=True,
1166             history=bool(n_tunnels < 100)
1167         )
1168         os.remove(tmp_fn1)
1169
1170         with open(tmp_fn1, 'w') as tmp_f1:
1171             for i in range(existing_tunnels, n_tunnels):
1172                 tmp_f1.write(
1173                     f"exec set interface unnumbered ipip{i} use {if1_n}\n"
1174                     f"exec set interface state ipip{i} up\n"
1175                     f"exec ip route add "
1176                     f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
1177                     f"via ipip{i}\n"
1178                 )
1179         vat.execute_script(
1180             tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1181             copy_on_execute=True,
1182             history=bool(n_tunnels < 100)
1183         )
1184         os.remove(tmp_fn1)
1185
1186         return ckeys, ikeys
1187
1188     @staticmethod
1189     def _ipsec_create_tunnel_interfaces_dut2_vat(
1190             nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1191             ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1192         """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
1193
1194         This method accesses keys generated by DUT1 method
1195         and does not return anything.
1196
1197         :param nodes: VPP nodes to create tunnel interfaces.
1198         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1199             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1200             IPv4/IPv6 address (ip2).
1201         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1202             interface key from topology file.
1203         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1204         :param crypto_alg: The encryption algorithm name.
1205         :param ckeys: List of encryption keys.
1206         :param integ_alg: The integrity algorithm name.
1207         :param ikeys: List of integrity keys.
1208         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1209         :param addr_incr: IP / IPv6 address incremental step.
1210         :param existing_tunnels: Number of tunnel interfaces before creation.
1211             Useful mainly for reconf tests. Default 0.
1212         :type nodes: dict
1213         :type tun_ips: dict
1214         :type if2_key: str
1215         :type n_tunnels: int
1216         :type crypto_alg: CryptoAlg
1217         :type ckeys: Sequence[bytes]
1218         :type integ_alg: Optional[IntegAlg]
1219         :type ikeys: Sequence[bytes]
1220         :type addr_incr: int
1221         :type spi_d: dict
1222         :type existing_tunnels: int
1223         """
1224         tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1225         if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1226
1227         vat = VatExecutor()
1228         with open(tmp_fn2, 'w') as tmp_f2:
1229             if not existing_tunnels:
1230                 tmp_f2.write(
1231                     f"exec set interface ip address {if2_n}"
1232                     f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1233                 )
1234             for i in range(existing_tunnels, n_tunnels):
1235                 if integ_alg:
1236                     integ = f"integ-alg {integ_alg.alg_name} " \
1237                         f"integ-key {ikeys[i].hex()} "
1238                 else:
1239                     integ = u""
1240                 tmp_f2.write(
1241                     f"exec create ipip tunnel "
1242                     f"src {tun_ips[u'ip2']} "
1243                     f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1244                     f"p2p\n"
1245                     f"exec ipsec sa add {100000 + i} "
1246                     f"spi {spi_d[u'spi_2'] + i} "
1247                     f"crypto-alg {crypto_alg.alg_name} "
1248                     f"crypto-key {ckeys[i].hex()} "
1249                     f"{integ}"
1250                     f"esp\n"
1251                     f"exec ipsec sa add {i} "
1252                     f"spi {spi_d[u'spi_1'] + i} "
1253                     f"crypto-alg {crypto_alg.alg_name} "
1254                     f"crypto-key {ckeys[i].hex()} "
1255                     f"{integ}"
1256                     f"esp\n"
1257                     f"exec ipsec tunnel protect ipip{i} "
1258                     f"sa-out {100000 + i} "
1259                     f"sa-in {i} "
1260                     f"add\n"
1261                 )
1262         vat.execute_script(
1263             tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1264             copy_on_execute=True,
1265             history=bool(n_tunnels < 100)
1266         )
1267         os.remove(tmp_fn2)
1268
1269         with open(tmp_fn2, 'w') as tmp_f2:
1270             if not existing_tunnels:
1271                 tmp_f2.write(
1272                     f"exec ip route add {tun_ips[u'ip1']}/8 "
1273                     f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1274                 )
1275             for i in range(existing_tunnels, n_tunnels):
1276                 tmp_f2.write(
1277                     f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1278                     f"exec set interface state ipip{i} up\n"
1279                     f"exec ip route add "
1280                     f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1281                     f"via ipip{i}\n"
1282                 )
1283         vat.execute_script(
1284             tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1285             copy_on_execute=True,
1286             history=bool(n_tunnels < 100)
1287         )
1288         os.remove(tmp_fn2)
1289
1290     @staticmethod
1291     def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1292         """Create loopback interface and set IP address on VPP node 1 interface
1293         using PAPI.
1294
1295         :param nodes: VPP nodes to create tunnel interfaces.
1296         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1297             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1298             IPv4/IPv6 address (ip2).
1299         :param if1_key: VPP node 1 interface key from topology file.
1300         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1301             interface key from topology file.
1302         :type nodes: dict
1303         :type tun_ips: dict
1304         :type if1_key: str
1305         :type if2_key: str
1306         """
1307         with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1308             # Create loopback interface on DUT1, set it to up state
1309             cmd = u"create_loopback_instance"
1310             args = dict(
1311                 mac_address=0,
1312                 is_specified=False,
1313                 user_instance=0,
1314             )
1315             err_msg = f"Failed to create loopback interface " \
1316                 f"on host {nodes[u'DUT1'][u'host']}"
1317             loop_sw_if_idx = papi_exec.add(cmd, **args). \
1318                 get_sw_if_index(err_msg)
1319             cmd = u"sw_interface_set_flags"
1320             args = dict(
1321                 sw_if_index=loop_sw_if_idx,
1322                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1323             )
1324             err_msg = f"Failed to set loopback interface state up " \
1325                 f"on host {nodes[u'DUT1'][u'host']}"
1326             papi_exec.add(cmd, **args).get_reply(err_msg)
1327             # Set IP address on VPP node 1 interface
1328             cmd = u"sw_interface_add_del_address"
1329             args = dict(
1330                 sw_if_index=InterfaceUtil.get_interface_index(
1331                     nodes[u"DUT1"], if1_key
1332                 ),
1333                 is_add=True,
1334                 del_all=False,
1335                 prefix=IPUtil.create_prefix_object(
1336                     tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1337                     else 24
1338                 )
1339             )
1340             err_msg = f"Failed to set IP address on interface {if1_key} " \
1341                 f"on host {nodes[u'DUT1'][u'host']}"
1342             papi_exec.add(cmd, **args).get_reply(err_msg)
1343             cmd2 = u"ip_neighbor_add_del"
1344             args2 = dict(
1345                 is_add=1,
1346                 neighbor=dict(
1347                     sw_if_index=Topology.get_interface_sw_index(
1348                         nodes[u"DUT1"], if1_key
1349                     ),
1350                     flags=1,
1351                     mac_address=str(
1352                         Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1353                         if u"DUT2" in nodes.keys()
1354                         else Topology.get_interface_mac(
1355                             nodes[u"TG"], if2_key
1356                         )
1357                     ),
1358                     ip_address=tun_ips[u"ip2"].compressed
1359                 )
1360             )
1361             err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1362             papi_exec.add(cmd2, **args2).get_reply(err_msg)
1363
1364             return loop_sw_if_idx
1365
1366     @staticmethod
1367     def _ipsec_create_tunnel_interfaces_dut1_papi(
1368             nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1369             raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1370         """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1371
1372         Generate random keys and return them (so DUT2 or TG can decrypt).
1373
1374         :param nodes: VPP nodes to create tunnel interfaces.
1375         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1376             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1377             IPv4/IPv6 address (ip2).
1378         :param if1_key: VPP node 1 interface key from topology file.
1379         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1380             interface key from topology file.
1381         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1382         :param crypto_alg: The encryption algorithm name.
1383         :param integ_alg: The integrity algorithm name.
1384         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1385             first tunnel in direction node2->node1.
1386         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1387         :param addr_incr: IP / IPv6 address incremental step.
1388         :param existing_tunnels: Number of tunnel interfaces before creation.
1389             Useful mainly for reconf tests. Default 0.
1390         :type nodes: dict
1391         :type tun_ips: dict
1392         :type if1_key: str
1393         :type if2_key: str
1394         :type n_tunnels: int
1395         :type crypto_alg: CryptoAlg
1396         :type integ_alg: Optional[IntegAlg]
1397         :type raddr_ip2: IPv4Address or IPv6Address
1398         :type addr_incr: int
1399         :type spi_d: dict
1400         :type existing_tunnels: int
1401         :returns: Generated ckeys and ikeys.
1402         :rtype: List[bytes], List[bytes]
1403         """
1404         if not existing_tunnels:
1405             loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1406                 nodes, tun_ips, if1_key, if2_key
1407             )
1408         else:
1409             loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1410                 nodes[u"DUT1"], u"loop0"
1411             )
1412         with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1413             # Configure IP addresses on loop0 interface
1414             cmd = u"sw_interface_add_del_address"
1415             args = dict(
1416                 sw_if_index=loop_sw_if_idx,
1417                 is_add=True,
1418                 del_all=False,
1419                 prefix=None
1420             )
1421             for i in range(existing_tunnels, n_tunnels):
1422                 args[u"prefix"] = IPUtil.create_prefix_object(
1423                     tun_ips[u"ip1"] + i * addr_incr,
1424                     128 if tun_ips[u"ip1"].version == 6 else 32
1425                 )
1426                 papi_exec.add(
1427                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1428                 )
1429             # Configure IPIP tunnel interfaces
1430             cmd = u"ipip_add_tunnel"
1431             ipip_tunnel = dict(
1432                 instance=Constants.BITWISE_NON_ZERO,
1433                 src=None,
1434                 dst=None,
1435                 table_id=0,
1436                 flags=int(
1437                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1438                 ),
1439                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1440                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1441             )
1442             args = dict(
1443                 tunnel=ipip_tunnel
1444             )
1445             ipip_tunnels = [None] * existing_tunnels
1446             for i in range(existing_tunnels, n_tunnels):
1447                 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1448                     tun_ips[u"ip1"] + i * addr_incr
1449                 )
1450                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1451                     tun_ips[u"ip2"]
1452                 )
1453                 papi_exec.add(
1454                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1455                 )
1456             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1457                 f" {nodes[u'DUT1'][u'host']}"
1458             ipip_tunnels.extend(
1459                 [
1460                     reply[u"sw_if_index"]
1461                     for reply in papi_exec.get_replies(err_msg)
1462                     if u"sw_if_index" in reply
1463                 ]
1464             )
1465             # Configure IPSec SAD entries
1466             ckeys = [bytes()] * existing_tunnels
1467             ikeys = [bytes()] * existing_tunnels
1468             cmd = u"ipsec_sad_entry_add"
1469             c_key = dict(
1470                 length=0,
1471                 data=None
1472             )
1473             i_key = dict(
1474                 length=0,
1475                 data=None
1476             )
1477             sad_entry = dict(
1478                 sad_id=None,
1479                 spi=None,
1480                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1481                 crypto_algorithm=crypto_alg.alg_int_repr,
1482                 crypto_key=c_key,
1483                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1484                 integrity_key=i_key,
1485                 flags=None,
1486                 tunnel=dict(
1487                     src=0,
1488                     dst=0,
1489                     table_id=0,
1490                     encap_decap_flags=int(
1491                         TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1492                     ),
1493                     dscp=int(IpDscp.IP_API_DSCP_CS0),
1494                 ),
1495                 salt=0,
1496                 udp_src_port=IPSEC_UDP_PORT_NONE,
1497                 udp_dst_port=IPSEC_UDP_PORT_NONE,
1498             )
1499             args = dict(entry=sad_entry)
1500             for i in range(existing_tunnels, n_tunnels):
1501                 ckeys.append(
1502                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1503                 )
1504                 ikeys.append(
1505                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1506                 )
1507                 # SAD entry for outband / tx path
1508                 args[u"entry"][u"sad_id"] = i
1509                 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1510
1511                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1512                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1513                 if integ_alg:
1514                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1515                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1516                 args[u"entry"][u"flags"] = int(
1517                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1518                 )
1519                 papi_exec.add(
1520                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1521                 )
1522                 # SAD entry for inband / rx path
1523                 args[u"entry"][u"sad_id"] = 100000 + i
1524                 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1525
1526                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1527                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1528                 if integ_alg:
1529                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1530                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1531                 args[u"entry"][u"flags"] = int(
1532                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1533                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1534                 )
1535                 papi_exec.add(
1536                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1537                 )
1538             err_msg = f"Failed to add IPsec SAD entries on host" \
1539                 f" {nodes[u'DUT1'][u'host']}"
1540             papi_exec.get_replies(err_msg)
1541             # Add protection for tunnels with IPSEC
1542             cmd = u"ipsec_tunnel_protect_update"
1543             n_hop = dict(
1544                 address=0,
1545                 via_label=MPLS_LABEL_INVALID,
1546                 obj_id=Constants.BITWISE_NON_ZERO
1547             )
1548             ipsec_tunnel_protect = dict(
1549                 sw_if_index=None,
1550                 nh=n_hop,
1551                 sa_out=None,
1552                 n_sa_in=1,
1553                 sa_in=None
1554             )
1555             args = dict(
1556                 tunnel=ipsec_tunnel_protect
1557             )
1558             for i in range(existing_tunnels, n_tunnels):
1559                 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1560                 args[u"tunnel"][u"sa_out"] = i
1561                 args[u"tunnel"][u"sa_in"] = [100000 + i]
1562                 papi_exec.add(
1563                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1564                 )
1565             err_msg = f"Failed to add protection for tunnels with IPSEC " \
1566                 f"on host {nodes[u'DUT1'][u'host']}"
1567             papi_exec.get_replies(err_msg)
1568
1569             # Configure unnumbered interfaces
1570             cmd = u"sw_interface_set_unnumbered"
1571             args = dict(
1572                 is_add=True,
1573                 sw_if_index=InterfaceUtil.get_interface_index(
1574                     nodes[u"DUT1"], if1_key
1575                 ),
1576                 unnumbered_sw_if_index=0
1577             )
1578             for i in range(existing_tunnels, n_tunnels):
1579                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1580                 papi_exec.add(
1581                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1582                 )
1583             # Set interfaces up
1584             cmd = u"sw_interface_set_flags"
1585             args = dict(
1586                 sw_if_index=0,
1587                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1588             )
1589             for i in range(existing_tunnels, n_tunnels):
1590                 args[u"sw_if_index"] = ipip_tunnels[i]
1591                 papi_exec.add(
1592                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1593                 )
1594             # Configure IP routes
1595             cmd = u"ip_route_add_del"
1596             args = dict(
1597                 is_add=1,
1598                 is_multipath=0,
1599                 route=None
1600             )
1601             for i in range(existing_tunnels, n_tunnels):
1602                 args[u"route"] = IPUtil.compose_vpp_route_structure(
1603                     nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1604                     prefix_len=128 if raddr_ip2.version == 6 else 32,
1605                     interface=ipip_tunnels[i]
1606                 )
1607                 papi_exec.add(
1608                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1609                 )
1610             err_msg = f"Failed to add IP routes on host " \
1611                 f"{nodes[u'DUT1'][u'host']}"
1612             papi_exec.get_replies(err_msg)
1613
1614         return ckeys, ikeys
1615
1616     @staticmethod
1617     def _ipsec_create_tunnel_interfaces_dut2_papi(
1618             nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1619             ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1620         """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1621
1622         This method accesses keys generated by DUT1 method
1623         and does not return anything.
1624
1625         :param nodes: VPP nodes to create tunnel interfaces.
1626         :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1627             IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1628             IPv4/IPv6 address (ip2).
1629         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1630             interface key from topology file.
1631         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1632         :param crypto_alg: The encryption algorithm name.
1633         :param ckeys: List of encryption keys.
1634         :param integ_alg: The integrity algorithm name.
1635         :param ikeys: List of integrity keys.
1636         :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1637         :param addr_incr: IP / IPv6 address incremental step.
1638         :param existing_tunnels: Number of tunnel interfaces before creation.
1639             Useful mainly for reconf tests. Default 0.
1640         :type nodes: dict
1641         :type tun_ips: dict
1642         :type if2_key: str
1643         :type n_tunnels: int
1644         :type crypto_alg: CryptoAlg
1645         :type ckeys: Sequence[bytes]
1646         :type integ_alg: Optional[IntegAlg]
1647         :type ikeys: Sequence[bytes]
1648         :type addr_incr: int
1649         :type spi_d: dict
1650         :type existing_tunnels: int
1651         """
1652         with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1653             if not existing_tunnels:
1654                 # Set IP address on VPP node 2 interface
1655                 cmd = u"sw_interface_add_del_address"
1656                 args = dict(
1657                     sw_if_index=InterfaceUtil.get_interface_index(
1658                         nodes[u"DUT2"], if2_key
1659                     ),
1660                     is_add=True,
1661                     del_all=False,
1662                     prefix=IPUtil.create_prefix_object(
1663                         tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1664                         else 24
1665                     )
1666                 )
1667                 err_msg = f"Failed to set IP address on interface {if2_key} " \
1668                     f"on host {nodes[u'DUT2'][u'host']}"
1669                 papi_exec.add(cmd, **args).get_reply(err_msg)
1670             # Configure IPIP tunnel interfaces
1671             cmd = u"ipip_add_tunnel"
1672             ipip_tunnel = dict(
1673                 instance=Constants.BITWISE_NON_ZERO,
1674                 src=None,
1675                 dst=None,
1676                 table_id=0,
1677                 flags=int(
1678                     TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1679                 ),
1680                 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1681                 dscp=int(IpDscp.IP_API_DSCP_CS0)
1682             )
1683             args = dict(
1684                 tunnel=ipip_tunnel
1685             )
1686             ipip_tunnels = [None] * existing_tunnels
1687             for i in range(existing_tunnels, n_tunnels):
1688                 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1689                     tun_ips[u"ip2"]
1690                 )
1691                 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1692                     tun_ips[u"ip1"] + i * addr_incr
1693                 )
1694                 papi_exec.add(
1695                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1696                 )
1697             err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1698                 f" {nodes[u'DUT2'][u'host']}"
1699             ipip_tunnels.extend(
1700                 [
1701                     reply[u"sw_if_index"]
1702                     for reply in papi_exec.get_replies(err_msg)
1703                     if u"sw_if_index" in reply
1704                 ]
1705             )
1706             # Configure IPSec SAD entries
1707             cmd = u"ipsec_sad_entry_add"
1708             c_key = dict(
1709                 length=0,
1710                 data=None
1711             )
1712             i_key = dict(
1713                 length=0,
1714                 data=None
1715             )
1716             sad_entry = dict(
1717                 sad_id=None,
1718                 spi=None,
1719                 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1720                 crypto_algorithm=crypto_alg.alg_int_repr,
1721                 crypto_key=c_key,
1722                 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1723                 integrity_key=i_key,
1724                 flags=None,
1725                 tunnel=dict(
1726                     src=0,
1727                     dst=0,
1728                     table_id=0,
1729                     encap_decap_flags=int(
1730                         TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1731                     ),
1732                     dscp=int(IpDscp.IP_API_DSCP_CS0),
1733                 ),
1734                 salt=0,
1735                 udp_src_port=IPSEC_UDP_PORT_NONE,
1736                 udp_dst_port=IPSEC_UDP_PORT_NONE,
1737             )
1738             args = dict(entry=sad_entry)
1739             for i in range(existing_tunnels, n_tunnels):
1740                 ckeys.append(
1741                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1742                 )
1743                 ikeys.append(
1744                     gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1745                 )
1746                 # SAD entry for outband / tx path
1747                 args[u"entry"][u"sad_id"] = 100000 + i
1748                 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1749
1750                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1751                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1752                 if integ_alg:
1753                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1754                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1755                 args[u"entry"][u"flags"] = int(
1756                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1757                 )
1758                 papi_exec.add(
1759                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1760                 )
1761                 # SAD entry for inband / rx path
1762                 args[u"entry"][u"sad_id"] = i
1763                 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1764
1765                 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1766                 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1767                 if integ_alg:
1768                     args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1769                     args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1770                 args[u"entry"][u"flags"] = int(
1771                     IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1772                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1773                 )
1774                 papi_exec.add(
1775                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1776                 )
1777             err_msg = f"Failed to add IPsec SAD entries on host" \
1778                 f" {nodes[u'DUT2'][u'host']}"
1779             papi_exec.get_replies(err_msg)
1780             # Add protection for tunnels with IPSEC
1781             cmd = u"ipsec_tunnel_protect_update"
1782             n_hop = dict(
1783                 address=0,
1784                 via_label=MPLS_LABEL_INVALID,
1785                 obj_id=Constants.BITWISE_NON_ZERO
1786             )
1787             ipsec_tunnel_protect = dict(
1788                 sw_if_index=None,
1789                 nh=n_hop,
1790                 sa_out=None,
1791                 n_sa_in=1,
1792                 sa_in=None
1793             )
1794             args = dict(
1795                 tunnel=ipsec_tunnel_protect
1796             )
1797             for i in range(existing_tunnels, n_tunnels):
1798                 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1799                 args[u"tunnel"][u"sa_out"] = 100000 + i
1800                 args[u"tunnel"][u"sa_in"] = [i]
1801                 papi_exec.add(
1802                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1803                 )
1804             err_msg = f"Failed to add protection for tunnels with IPSEC " \
1805                 f"on host {nodes[u'DUT2'][u'host']}"
1806             papi_exec.get_replies(err_msg)
1807
1808             if not existing_tunnels:
1809                 # Configure IP route
1810                 cmd = u"ip_route_add_del"
1811                 route = IPUtil.compose_vpp_route_structure(
1812                     nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1813                     prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1814                     interface=if2_key,
1815                     gateway=(tun_ips[u"ip2"] - 1).compressed
1816                 )
1817                 args = dict(
1818                     is_add=1,
1819                     is_multipath=0,
1820                     route=route
1821                 )
1822                 papi_exec.add(cmd, **args)
1823             # Configure unnumbered interfaces
1824             cmd = u"sw_interface_set_unnumbered"
1825             args = dict(
1826                 is_add=True,
1827                 sw_if_index=InterfaceUtil.get_interface_index(
1828                     nodes[u"DUT2"], if2_key
1829                 ),
1830                 unnumbered_sw_if_index=0
1831             )
1832             for i in range(existing_tunnels, n_tunnels):
1833                 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1834                 papi_exec.add(
1835                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1836                 )
1837             # Set interfaces up
1838             cmd = u"sw_interface_set_flags"
1839             args = dict(
1840                 sw_if_index=0,
1841                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1842             )
1843             for i in range(existing_tunnels, n_tunnels):
1844                 args[u"sw_if_index"] = ipip_tunnels[i]
1845                 papi_exec.add(
1846                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1847                 )
1848             # Configure IP routes
1849             cmd = u"ip_route_add_del"
1850             args = dict(
1851                 is_add=1,
1852                 is_multipath=0,
1853                 route=None
1854             )
1855             for i in range(existing_tunnels, n_tunnels):
1856                 args[u"route"] = IPUtil.compose_vpp_route_structure(
1857                     nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1858                     prefix_len=128 if raddr_ip1.version == 6 else 32,
1859                     interface=ipip_tunnels[i]
1860                 )
1861                 papi_exec.add(
1862                     cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1863                 )
1864             err_msg = f"Failed to add IP routes " \
1865                 f"on host {nodes[u'DUT2'][u'host']}"
1866             papi_exec.get_replies(err_msg)
1867
1868     @staticmethod
1869     def vpp_ipsec_create_tunnel_interfaces(
1870             nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1871             n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1872             existing_tunnels=0, return_keys=False):
1873         """Create multiple IPsec tunnel interfaces between two VPP nodes.
1874
1875         Some deployments (e.g. devicetest) need to know the generated keys.
1876         But other deployments (e.g. scale perf test) would get spammed
1877         if we returned keys every time.
1878
1879         :param nodes: VPP nodes to create tunnel interfaces.
1880         :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1881             address.
1882         :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1883             address.
1884         :param if1_key: VPP node 1 interface key from topology file.
1885         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1886             interface key from topology file.
1887         :param n_tunnels: Number of tunnel interfaces to be there at the end.
1888         :param crypto_alg: The encryption algorithm name.
1889         :param integ_alg: The integrity algorithm name.
1890         :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1891             first tunnel in direction node1->node2.
1892         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1893             first tunnel in direction node2->node1.
1894         :param raddr_range: Mask specifying range of Policy selector Remote
1895             IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1896             and to 128 in case of IPv6.
1897         :param existing_tunnels: Number of tunnel interfaces before creation.
1898             Useful mainly for reconf tests. Default 0.
1899         :param return_keys: Whether generated keys should be returned.
1900         :type nodes: dict
1901         :type tun_if1_ip_addr: str
1902         :type tun_if2_ip_addr: str
1903         :type if1_key: str
1904         :type if2_key: str
1905         :type n_tunnels: int
1906         :type crypto_alg: CryptoAlg
1907         :type integ_alg: Optonal[IntegAlg]
1908         :type raddr_ip1: string
1909         :type raddr_ip2: string
1910         :type raddr_range: int
1911         :type existing_tunnels: int
1912         :type return_keys: bool
1913         :returns: Ckeys, ikeys, spi_1, spi_2.
1914         :rtype: Optional[List[bytes], List[bytes], int, int]
1915         """
1916         n_tunnels = int(n_tunnels)
1917         existing_tunnels = int(existing_tunnels)
1918         spi_d = dict(
1919             spi_1=100000,
1920             spi_2=200000
1921         )
1922         tun_ips = dict(
1923             ip1=ip_address(tun_if1_ip_addr),
1924             ip2=ip_address(tun_if2_ip_addr)
1925         )
1926         raddr_ip1 = ip_address(raddr_ip1)
1927         raddr_ip2 = ip_address(raddr_ip2)
1928         addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1929             else 1 << (32 - raddr_range)
1930
1931         if n_tunnels - existing_tunnels > 10:
1932             ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1933                 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1934                 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1935             )
1936             if u"DUT2" in nodes.keys():
1937                 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1938                     nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1939                     integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1940                     existing_tunnels
1941                 )
1942         else:
1943             ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1944                 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1945                 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1946             )
1947             if u"DUT2" in nodes.keys():
1948                 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1949                     nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1950                     integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1951                     existing_tunnels
1952                 )
1953
1954         if return_keys:
1955             return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1956         return None
1957
1958     @staticmethod
1959     def _create_ipsec_script_files(dut, instances):
1960         """Create script files for configuring IPsec in containers
1961
1962         :param dut: DUT node on which to create the script files
1963         :param instances: number of containers on DUT node
1964         :type dut: string
1965         :type instances: int
1966         """
1967         scripts = []
1968         for cnf in range(0, instances):
1969             script_filename = (
1970                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1971             )
1972             scripts.append(open(script_filename, 'w'))
1973         return scripts
1974
1975     @staticmethod
1976     def _close_and_copy_ipsec_script_files(
1977             dut, nodes, instances, scripts):
1978         """Close created scripts and copy them to containers
1979
1980         :param dut: DUT node on which to create the script files
1981         :param nodes: VPP nodes
1982         :param instances: number of containers on DUT node
1983         :param scripts: dictionary holding the script files
1984         :type dut: string
1985         :type nodes: dict
1986         :type instances: int
1987         :type scripts: dict
1988         """
1989         for cnf in range(0, instances):
1990             scripts[cnf].close()
1991             script_filename = (
1992                 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1993             )
1994             scp_node(nodes[dut], script_filename, script_filename)
1995
1996
1997     @staticmethod
1998     def vpp_ipsec_create_tunnel_interfaces_in_containers(
1999             nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
2000             raddr_ip1, raddr_ip2, raddr_range, n_instances):
2001         """Create multiple IPsec tunnel interfaces between two VPP nodes.
2002
2003         :param nodes: VPP nodes to create tunnel interfaces.
2004         :param if1_ip_addr: VPP node 1 interface IP4 address.
2005         :param if2_ip_addr: VPP node 2 interface IP4 address.
2006         :param n_tunnels: Number of tunnell interfaces to create.
2007         :param crypto_alg: The encryption algorithm name.
2008         :param integ_alg: The integrity algorithm name.
2009         :param raddr_ip1: Policy selector remote IPv4 start address for the
2010             first tunnel in direction node1->node2.
2011         :param raddr_ip2: Policy selector remote IPv4 start address for the
2012             first tunnel in direction node2->node1.
2013         :param raddr_range: Mask specifying range of Policy selector Remote
2014             IPv4 addresses. Valid values are from 1 to 32.
2015         :param n_instances: Number of containers.
2016         :type nodes: dict
2017         :type if1_ip_addr: str
2018         :type if2_ip_addr: str
2019         :type n_tunnels: int
2020         :type crypto_alg: CryptoAlg
2021         :type integ_alg: Optional[IntegAlg]
2022         :type raddr_ip1: string
2023         :type raddr_ip2: string
2024         :type raddr_range: int
2025         :type n_instances: int
2026         """
2027         spi_1 = 100000
2028         spi_2 = 200000
2029         addr_incr = 1 << (32 - raddr_range)
2030
2031         dut1_scripts = IPsecUtil._create_ipsec_script_files(
2032             u"DUT1", n_instances
2033         )
2034         dut2_scripts = IPsecUtil._create_ipsec_script_files(
2035             u"DUT2", n_instances
2036         )
2037
2038         for cnf in range(0, n_instances):
2039             dut1_scripts[cnf].write(
2040                 u"create loopback interface\n"
2041                 u"set interface state loop0 up\n\n"
2042             )
2043             dut2_scripts[cnf].write(
2044                 f"ip route add {if1_ip_addr}/8 via "
2045                 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
2046             )
2047
2048         for tnl in range(0, n_tunnels):
2049             cnf = tnl % n_instances
2050             ckey = getattr(
2051                 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
2052             )
2053             integ = u""
2054             ikey = getattr(
2055                 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
2056             )
2057             if integ_alg:
2058                 integ = (
2059                     f"integ-alg {integ_alg.alg_name} "
2060                     f"local-integ-key {ikey} "
2061                     f"remote-integ-key {ikey} "
2062                 )
2063             # Configure tunnel end point(s) on left side
2064             dut1_scripts[cnf].write(
2065                 u"set interface ip address loop0 "
2066                 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
2067                 f"create ipsec tunnel "
2068                 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2069                 f"local-spi {spi_1 + tnl} "
2070                 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
2071                 f"remote-spi {spi_2 + tnl} "
2072                 f"crypto-alg {crypto_alg.alg_name} "
2073                 f"local-crypto-key {ckey} "
2074                 f"remote-crypto-key {ckey} "
2075                 f"instance {tnl // n_instances} "
2076                 f"salt 0x0 "
2077                 f"{integ} \n"
2078                 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
2079                 f"set interface state ipip{tnl // n_instances} up\n"
2080                 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
2081                 f"via ipip{tnl // n_instances}\n\n"
2082             )
2083             # Configure tunnel end point(s) on right side
2084             dut2_scripts[cnf].write(
2085                 f"set ip neighbor memif1/{cnf + 1} "
2086                 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
2087                 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
2088                 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
2089                 f"local-spi {spi_2 + tnl} "
2090                 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2091                 f"remote-spi {spi_1 + tnl} "
2092                 f"crypto-alg {crypto_alg.alg_name} "
2093                 f"local-crypto-key {ckey} "
2094                 f"remote-crypto-key {ckey} "
2095                 f"instance {tnl // n_instances} "
2096                 f"salt 0x0 "
2097                 f"{integ}\n"
2098                 f"set interface unnumbered ipip{tnl // n_instances} "
2099                 f"use memif1/{cnf + 1}\n"
2100                 f"set interface state ipip{tnl // n_instances} up\n"
2101                 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
2102                 f"via ipip{tnl // n_instances}\n\n"
2103             )
2104
2105         IPsecUtil._close_and_copy_ipsec_script_files(
2106             u"DUT1", nodes, n_instances, dut1_scripts)
2107         IPsecUtil._close_and_copy_ipsec_script_files(
2108             u"DUT2", nodes, n_instances, dut2_scripts)
2109
2110     @staticmethod
2111     def vpp_ipsec_add_multiple_tunnels(
2112             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
2113             tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range,
2114             tunnel_addr_incr=True):
2115         """Create multiple IPsec tunnels between two VPP nodes.
2116
2117         :param nodes: VPP nodes to create tunnels.
2118         :param interface1: Interface name or sw_if_index on node 1.
2119         :param interface2: Interface name or sw_if_index on node 2.
2120         :param n_tunnels: Number of tunnels to create.
2121         :param crypto_alg: The encryption algorithm name.
2122         :param integ_alg: The integrity algorithm name.
2123         :param tunnel_ip1: Tunnel node1 IPv4 address.
2124         :param tunnel_ip2: Tunnel node2 IPv4 address.
2125         :param raddr_ip1: Policy selector remote IPv4 start address for the
2126             first tunnel in direction node1->node2.
2127         :param raddr_ip2: Policy selector remote IPv4 start address for the
2128             first tunnel in direction node2->node1.
2129         :param raddr_range: Mask specifying range of Policy selector Remote
2130             IPv4 addresses. Valid values are from 1 to 32.
2131         :param tunnel_addr_incr: Enable or disable tunnel IP address
2132             incremental step.
2133         :type nodes: dict
2134         :type interface1: str or int
2135         :type interface2: str or int
2136         :type n_tunnels: int
2137         :type crypto_alg: CryptoAlg
2138         :type integ_alg: Optional[IntegAlg]
2139         :type tunnel_ip1: str
2140         :type tunnel_ip2: str
2141         :type raddr_ip1: string
2142         :type raddr_ip2: string
2143         :type raddr_range: int
2144         :type tunnel_addr_incr: bool
2145         """
2146         spd_id = 1
2147         p_hi = 100
2148         p_lo = 10
2149         sa_id_1 = 100000
2150         sa_id_2 = 200000
2151         spi_1 = 300000
2152         spi_2 = 400000
2153         dut1_local_outbound_range = ip_network(f"{tunnel_ip1}/8", False).\
2154             with_prefixlen
2155         dut1_remote_outbound_range = ip_network(f"{tunnel_ip2}/8", False).\
2156             with_prefixlen
2157
2158         crypto_key = gen_key(
2159             IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2160         ).decode()
2161         integ_key = gen_key(
2162             IPsecUtil.get_integ_alg_key_len(integ_alg)
2163         ).decode() if integ_alg else u""
2164
2165         rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
2166             if u"DUT2" in nodes.keys() \
2167             else Topology.get_interface_mac(nodes[u"TG"], interface2)
2168         IPsecUtil.vpp_ipsec_set_ip_route(
2169             nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
2170             interface1, raddr_range, rmac)
2171
2172         IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
2173         IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
2174         IPsecUtil.vpp_ipsec_add_spd_entry(
2175             nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
2176             proto=50, laddr_range=dut1_local_outbound_range,
2177             raddr_range=dut1_remote_outbound_range
2178         )
2179         IPsecUtil.vpp_ipsec_add_spd_entry(
2180             nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
2181             proto=50, laddr_range=dut1_remote_outbound_range,
2182             raddr_range=dut1_local_outbound_range
2183         )
2184
2185         IPsecUtil.vpp_ipsec_add_sad_entries(
2186             nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
2187             integ_alg, integ_key, tunnel_ip1, tunnel_ip2, tunnel_addr_incr
2188         )
2189
2190         IPsecUtil.vpp_ipsec_add_spd_entries(
2191             nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2192             action=PolicyAction.PROTECT, inbound=False,
2193             sa_id=ObjIncrement(sa_id_1, 1),
2194             raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2195         )
2196
2197         IPsecUtil.vpp_ipsec_add_sad_entries(
2198             nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
2199             integ_alg, integ_key, tunnel_ip2, tunnel_ip1, tunnel_addr_incr
2200         )
2201         IPsecUtil.vpp_ipsec_add_spd_entries(
2202             nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2203             action=PolicyAction.PROTECT, inbound=True,
2204             sa_id=ObjIncrement(sa_id_2, 1),
2205             raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2206         )
2207
2208         if u"DUT2" in nodes.keys():
2209             rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
2210             IPsecUtil.vpp_ipsec_set_ip_route(
2211                 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2212                 interface2, raddr_range, rmac)
2213
2214             IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2215             IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2216             IPsecUtil.vpp_ipsec_add_spd_entry(
2217                 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2218                 inbound=False, proto=50, laddr_range=dut1_remote_outbound_range,
2219                 raddr_range=dut1_local_outbound_range
2220             )
2221             IPsecUtil.vpp_ipsec_add_spd_entry(
2222                 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2223                 inbound=True, proto=50, laddr_range=dut1_local_outbound_range,
2224                 raddr_range=dut1_remote_outbound_range
2225             )
2226
2227             IPsecUtil.vpp_ipsec_add_sad_entries(
2228                 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2229                 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2,
2230                 tunnel_addr_incr
2231             )
2232             IPsecUtil.vpp_ipsec_add_spd_entries(
2233                 nodes[u"DUT2"], n_tunnels, spd_id,
2234                 priority=ObjIncrement(p_lo, 0),
2235                 action=PolicyAction.PROTECT, inbound=True,
2236                 sa_id=ObjIncrement(sa_id_1, 1),
2237                 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2238             )
2239
2240             IPsecUtil.vpp_ipsec_add_sad_entries(
2241                 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2242                 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1,
2243                 tunnel_addr_incr
2244             )
2245             IPsecUtil.vpp_ipsec_add_spd_entries(
2246                 nodes[u"DUT2"], n_tunnels, spd_id,
2247                 priority=ObjIncrement(p_lo, 0),
2248                 action=PolicyAction.PROTECT, inbound=False,
2249                 sa_id=ObjIncrement(sa_id_2, 1),
2250                 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2251             )
2252
2253     @staticmethod
2254     def vpp_ipsec_show_all(node):
2255         """Run "show ipsec all" debug CLI command.
2256
2257         :param node: Node to run command on.
2258         :type node: dict
2259         """
2260         PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2261
2262     @staticmethod
2263     def show_ipsec_security_association(node):
2264         """Show IPSec security association.
2265
2266         :param node: DUT node.
2267         :type node: dict
2268         """
2269         cmds = [
2270             u"ipsec_sa_v3_dump"
2271         ]
2272         PapiSocketExecutor.dump_and_log(node, cmds)
2273
2274     @staticmethod
2275     def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2276         """Ipsec flow enable rss action.
2277
2278         :param node: DUT node.
2279         :param proto: The flow protocol.
2280         :param type: RSS type.
2281         :param function: RSS function.
2282
2283         :type node: dict
2284         :type proto: str
2285         :type type: str
2286         :type function: str
2287         :returns: flow_index.
2288         """
2289         # TODO: to be fixed to use full PAPI when it is ready in VPP
2290         cmd = f"test flow add src-ip any proto {proto} rss function " \
2291             f"{function} rss types {type}"
2292         stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2293         flow_index = stdout.split()[1]
2294
2295         return flow_index
2296
2297     @staticmethod
2298     def vpp_create_ipsec_flows_on_dut(
2299             node, n_flows, rx_queues, spi_start, interface):
2300         """Create mutiple ipsec flows and enable flows onto interface.
2301
2302         :param node: DUT node.
2303         :param n_flows: Number of flows to create.
2304         :param rx_queues: NUmber of RX queues.
2305         :param spi_start: The start spi.
2306         :param interface: Name of the interface.
2307
2308         :type node: dict
2309         :type n_flows: int
2310         :type rx_queues: int
2311         :type spi_start: int
2312         :type interface: str
2313         :returns: flow_index.
2314         """
2315
2316         for i in range(0, n_flows):
2317             rx_queue = i%rx_queues
2318
2319             spi = spi_start + i
2320             flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2321                     node, "ESP", spi, "redirect-to-queue", value=rx_queue)
2322             FlowUtil.vpp_flow_enable(node, interface, flow_index)