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