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