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