FIX: IPsecUtil - create IP object separately in ever iteration
[csit.git] / resources / libraries / python / IPsecUtil.py
1 # Copyright (c) 2019 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 random import choice
19 from string import letters
20
21 from enum import Enum, IntEnum
22 from ipaddress import ip_network, ip_address
23
24 from resources.libraries.python.IPUtil import IPUtil
25 from resources.libraries.python.InterfaceUtil import InterfaceUtil
26 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
27 from resources.libraries.python.topology import Topology
28 from resources.libraries.python.VatExecutor import VatExecutor
29
30
31 def gen_key(length):
32     """Generate random string as a key.
33
34     :param length: Length of generated payload.
35     :type length: int
36     :returns: The generated payload.
37     :rtype: str
38     """
39     return ''.join(choice(letters) for _ in range(length))
40
41
42 class PolicyAction(Enum):
43     """Policy actions."""
44     BYPASS = ('bypass', 0)
45     DISCARD = ('discard', 1)
46     PROTECT = ('protect', 3)
47
48     def __init__(self, policy_name, policy_int_repr):
49         self.policy_name = policy_name
50         self.policy_int_repr = policy_int_repr
51
52
53 class CryptoAlg(Enum):
54     """Encryption algorithms."""
55     AES_CBC_128 = ('aes-cbc-128', 1, 'AES-CBC', 16)
56     AES_CBC_256 = ('aes-cbc-256', 3, 'AES-CBC', 32)
57     AES_GCM_128 = ('aes-gcm-128', 7, 'AES-GCM', 16)
58     AES_GCM_256 = ('aes-gcm-256', 9, 'AES-GCM', 32)
59
60     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
61         self.alg_name = alg_name
62         self.alg_int_repr = alg_int_repr
63         self.scapy_name = scapy_name
64         self.key_len = key_len
65
66
67 class IntegAlg(Enum):
68     """Integrity algorithm."""
69     SHA_256_128 = ('sha-256-128', 4, 'SHA2-256-128', 32)
70     SHA_512_256 = ('sha-512-256', 6, 'SHA2-512-256', 64)
71
72     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
73         self.alg_name = alg_name
74         self.alg_int_repr = alg_int_repr
75         self.scapy_name = scapy_name
76         self.key_len = key_len
77
78
79 class IPsecProto(IntEnum):
80     """IPsec protocol."""
81     ESP = 1
82     SEC_AH = 0
83
84
85 class IPsecSadFlags(IntEnum):
86     """IPsec Security Association Database flags."""
87     IPSEC_API_SAD_FLAG_NONE = 0
88     IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
89     IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
90
91
92 class IPsecUtil(object):
93     """IPsec utilities."""
94
95     @staticmethod
96     def policy_action_bypass():
97         """Return policy action bypass.
98
99         :returns: PolicyAction enum BYPASS object.
100         :rtype: PolicyAction
101         """
102         return PolicyAction.BYPASS
103
104     @staticmethod
105     def policy_action_discard():
106         """Return policy action discard.
107
108         :returns: PolicyAction enum DISCARD object.
109         :rtype: PolicyAction
110         """
111         return PolicyAction.DISCARD
112
113     @staticmethod
114     def policy_action_protect():
115         """Return policy action protect.
116
117         :returns: PolicyAction enum PROTECT object.
118         :rtype: PolicyAction
119         """
120         return PolicyAction.PROTECT
121
122     @staticmethod
123     def crypto_alg_aes_cbc_128():
124         """Return encryption algorithm aes-cbc-128.
125
126         :returns: CryptoAlg enum AES_CBC_128 object.
127         :rtype: CryptoAlg
128         """
129         return CryptoAlg.AES_CBC_128
130
131     @staticmethod
132     def crypto_alg_aes_cbc_256():
133         """Return encryption algorithm aes-cbc-256.
134
135         :returns: CryptoAlg enum AES_CBC_256 object.
136         :rtype: CryptoAlg
137         """
138         return CryptoAlg.AES_CBC_256
139
140     @staticmethod
141     def crypto_alg_aes_gcm_128():
142         """Return encryption algorithm aes-gcm-128.
143
144         :returns: CryptoAlg enum AES_GCM_128 object.
145         :rtype: CryptoAlg
146         """
147         return CryptoAlg.AES_GCM_128
148
149     @staticmethod
150     def crypto_alg_aes_gcm_256():
151         """Return encryption algorithm aes-gcm-256.
152
153         :returns: CryptoAlg enum AES_GCM_128 object.
154         :rtype: CryptoAlg
155         """
156         return CryptoAlg.AES_GCM_256
157
158     @staticmethod
159     def get_crypto_alg_key_len(crypto_alg):
160         """Return encryption algorithm key length.
161
162         :param crypto_alg: Encryption algorithm.
163         :type crypto_alg: CryptoAlg
164         :returns: Key length.
165         :rtype: int
166         """
167         return crypto_alg.key_len
168
169     @staticmethod
170     def get_crypto_alg_scapy_name(crypto_alg):
171         """Return encryption algorithm scapy name.
172
173         :param crypto_alg: Encryption algorithm.
174         :type crypto_alg: CryptoAlg
175         :returns: Algorithm scapy name.
176         :rtype: str
177         """
178         return crypto_alg.scapy_name
179
180     @staticmethod
181     def integ_alg_sha_256_128():
182         """Return integrity algorithm SHA-256-128.
183
184         :returns: IntegAlg enum SHA_256_128 object.
185         :rtype: IntegAlg
186         """
187         return IntegAlg.SHA_256_128
188
189     @staticmethod
190     def integ_alg_sha_512_256():
191         """Return integrity algorithm SHA-512-256.
192
193         :returns: IntegAlg enum SHA_512_256 object.
194         :rtype: IntegAlg
195         """
196         return IntegAlg.SHA_512_256
197
198     @staticmethod
199     def get_integ_alg_key_len(integ_alg):
200         """Return integrity algorithm key length.
201
202         :param integ_alg: Integrity algorithm.
203         :type integ_alg: IntegAlg
204         :returns: Key length.
205         :rtype: int
206         """
207         return integ_alg.key_len
208
209     @staticmethod
210     def get_integ_alg_scapy_name(integ_alg):
211         """Return integrity algorithm scapy name.
212
213         :param integ_alg: Integrity algorithm.
214         :type integ_alg: IntegAlg
215         :returns: Algorithm scapy name.
216         :rtype: str
217         """
218         return integ_alg.scapy_name
219
220     @staticmethod
221     def ipsec_proto_esp():
222         """Return IPSec protocol ESP.
223
224         :returns: IPsecProto enum ESP object.
225         :rtype: IPsecProto
226         """
227         return int(IPsecProto.ESP)
228
229     @staticmethod
230     def ipsec_proto_ah():
231         """Return IPSec protocol AH.
232
233         :returns: IPsecProto enum AH object.
234         :rtype: IPsecProto
235         """
236         return int(IPsecProto.SEC_AH)
237
238     @staticmethod
239     def vpp_ipsec_select_backend(node, protocol, index=1):
240         """Select IPsec backend.
241
242         :param node: VPP node to select IPsec backend on.
243         :param protocol: IPsec protocol.
244         :param index: Backend index.
245         :type node: dict
246         :type protocol: IPsecProto
247         :type index: int
248         :raises RuntimeError: If failed to select IPsec backend or if no API
249             reply received.
250         """
251         cmd = 'ipsec_select_backend'
252         err_msg = 'Failed to select IPsec backend on host {host}'.format(
253             host=node['host'])
254         args = dict(
255             protocol=protocol,
256             index=index
257         )
258         with PapiSocketExecutor(node) as papi_exec:
259             papi_exec.add(cmd, **args).get_reply(err_msg)
260
261     @staticmethod
262     def vpp_ipsec_backend_dump(node):
263         """Dump IPsec backends.
264
265         :param node: VPP node to dump IPsec backend on.
266         :type node: dict
267         """
268         err_msg = 'Failed to dump IPsec backends on host {host}'.format(
269             host=node['host'])
270         with PapiSocketExecutor(node) as papi_exec:
271             papi_exec.add('ipsec_backend_dump').get_details(err_msg)
272
273     @staticmethod
274     def vpp_ipsec_add_sad_entry(
275             node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
276             integ_key='', tunnel_src=None, tunnel_dst=None):
277         """Create Security Association Database entry on the VPP node.
278
279         :param node: VPP node to add SAD entry on.
280         :param sad_id: SAD entry ID.
281         :param spi: Security Parameter Index of this SAD entry.
282         :param crypto_alg: The encryption algorithm name.
283         :param crypto_key: The encryption key string.
284         :param integ_alg: The integrity algorithm name.
285         :param integ_key: The integrity key string.
286         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
287             specified ESP transport mode is used.
288         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
289             not specified ESP transport mode is used.
290         :type node: dict
291         :type sad_id: int
292         :type spi: int
293         :type crypto_alg: CryptoAlg
294         :type crypto_key: str
295         :type integ_alg: IntegAlg
296         :type integ_key: str
297         :type tunnel_src: str
298         :type tunnel_dst: str
299         """
300         ckey = dict(
301             length=len(crypto_key),
302             data=crypto_key
303         )
304         ikey = dict(
305             length=len(integ_key),
306             data=integ_key if integ_key else 0
307         )
308
309         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
310         if tunnel_src and tunnel_dst:
311             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
312             src_addr = ip_address(unicode(tunnel_src))
313             dst_addr = ip_address(unicode(tunnel_dst))
314             if src_addr.version == 6:
315                 flags = \
316                     flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
317         else:
318             src_addr = ''
319             dst_addr = ''
320
321         cmd = 'ipsec_sad_entry_add_del'
322         err_msg = 'Failed to add Security Association Database entry on ' \
323                   'host {host}'.format(host=node['host'])
324         sad_entry = dict(
325             sad_id=int(sad_id),
326             spi=int(spi),
327             crypto_algorithm=crypto_alg.alg_int_repr,
328             crypto_key=ckey,
329             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
330             integrity_key=ikey,
331             flags=flags,
332             tunnel_src=str(src_addr),
333             tunnel_dst=str(dst_addr),
334             protocol=int(IPsecProto.ESP)
335         )
336         args = dict(
337             is_add=1,
338             entry=sad_entry
339         )
340         with PapiSocketExecutor(node) as papi_exec:
341             papi_exec.add(cmd, **args).get_reply(err_msg)
342
343     @staticmethod
344     def vpp_ipsec_add_sad_entries(
345             node, n_entries, sad_id, spi, crypto_alg, crypto_key,
346             integ_alg=None, integ_key='', tunnel_src=None, tunnel_dst=None):
347         """Create multiple Security Association Database entries on VPP node.
348
349         :param node: VPP node to add SAD entry on.
350         :param n_entries: Number of SAD entries to be created.
351         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
352             id incremented by 1.
353         :param spi: Security Parameter Index of first SAD entry. All subsequent
354             SAD entries will have spi incremented by 1.
355         :param crypto_alg: The encryption algorithm name.
356         :param crypto_key: The encryption key string.
357         :param integ_alg: The integrity algorithm name.
358         :param integ_key: The integrity key string.
359         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
360             specified ESP transport mode is used.
361         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
362             not specified ESP transport mode is used.
363         :type node: dict
364         :type n_entries: int
365         :type sad_id: int
366         :type spi: int
367         :type crypto_alg: CryptoAlg
368         :type crypto_key: str
369         :type integ_alg: IntegAlg
370         :type integ_key: str
371         :type tunnel_src: str
372         :type tunnel_dst: str
373         """
374         if tunnel_src and tunnel_dst:
375             src_addr = ip_address(unicode(tunnel_src))
376             dst_addr = ip_address(unicode(tunnel_dst))
377         else:
378             src_addr = ''
379             dst_addr = ''
380
381         addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
382             else 1 << (32 - 24)
383
384         if int(n_entries) > 10:
385             tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.\
386                 format(sad_id)
387
388             with open(tmp_filename, 'w') as tmp_file:
389                 for i in xrange(n_entries):
390                     integ = (
391                         'integ-alg {integ_alg} integ-key {integ_key}'.format(
392                             integ_alg=integ_alg.alg_name,
393                             integ_key=integ_key.encode('hex'))
394                         if integ_alg else '')
395                     tunnel = (
396                         'tunnel-src {laddr} tunnel-dst {raddr}'.format(
397                             laddr=src_addr + i * addr_incr,
398                             raddr=dst_addr + i * addr_incr)
399                         if tunnel_src and tunnel_dst else '')
400                     conf = (
401                         'exec ipsec sa add {sad_id} esp spi {spi} '
402                         'crypto-alg {crypto_alg} crypto-key {crypto_key} '
403                         '{integ} {tunnel}\n'.format(
404                             sad_id=sad_id + i,
405                             spi=spi + i,
406                             crypto_alg=crypto_alg.alg_name,
407                             crypto_key=crypto_key.encode('hex'),
408                             integ=integ,
409                             tunnel=tunnel))
410                     tmp_file.write(conf)
411             vat = VatExecutor()
412             vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
413                                copy_on_execute=True)
414             os.remove(tmp_filename)
415             return
416
417         ckey = dict(
418             length=len(crypto_key),
419             data=crypto_key
420         )
421         ikey = dict(
422             length=len(integ_key),
423             data=integ_key if integ_key else 0
424         )
425
426         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
427         if tunnel_src and tunnel_dst:
428             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
429             if src_addr.version == 6:
430                 flags = flags | int(
431                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
432
433         cmd = 'ipsec_sad_entry_add_del'
434         err_msg = 'Failed to add Security Association Database entry on ' \
435                   'host {host}'.format(host=node['host'])
436
437         sad_entry = dict(
438             sad_id=int(sad_id),
439             spi=int(spi),
440             crypto_algorithm=crypto_alg.alg_int_repr,
441             crypto_key=ckey,
442             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
443             integrity_key=ikey,
444             flags=flags,
445             tunnel_src=str(src_addr),
446             tunnel_dst=str(dst_addr),
447             protocol=int(IPsecProto.ESP)
448         )
449         args = dict(
450             is_add=1,
451             entry=sad_entry
452         )
453         with PapiSocketExecutor(node) as papi_exec:
454             for i in xrange(n_entries):
455                 args['entry']['sad_id'] = int(sad_id) + i
456                 args['entry']['spi'] = int(spi) + i
457                 args['entry']['tunnel_src'] = str(src_addr + i * addr_incr) \
458                     if tunnel_src and tunnel_dst else src_addr
459                 args['entry']['tunnel_dst'] = str(dst_addr + i * addr_incr) \
460                     if tunnel_src and tunnel_dst else dst_addr
461                 history = False if 1 < i < n_entries - 1 else True
462                 papi_exec.add(cmd, history=history, **args)
463             papi_exec.get_replies(err_msg)
464
465     @staticmethod
466     def vpp_ipsec_set_ip_route(
467             node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
468             raddr_range):
469         """Set IP address and route on interface.
470
471         :param node: VPP node to add config on.
472         :param n_tunnels: Number of tunnels to create.
473         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
474         :param traffic_addr: Traffic destination IP address to route.
475         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
476         :param interface: Interface key on node 1.
477         :param raddr_range: Mask specifying range of Policy selector Remote IP
478             addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
479             in case of IPv6.
480         :type node: dict
481         :type n_tunnels: int
482         :type tunnel_src: str
483         :type traffic_addr: str
484         :type tunnel_dst: str
485         :type interface: str
486         :type raddr_range: int
487         """
488         laddr = ip_address(unicode(tunnel_src))
489         raddr = ip_address(unicode(tunnel_dst))
490         taddr = ip_address(unicode(traffic_addr))
491         addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
492             else 1 << (32 - raddr_range)
493
494         if int(n_tunnels) > 10:
495             tmp_filename = '/tmp/ipsec_set_ip.script'
496
497             with open(tmp_filename, 'w') as tmp_file:
498                 for i in xrange(n_tunnels):
499                     conf = (
500                         'exec set interface ip address {interface} '
501                         '{laddr}/{laddr_l}\n'
502                         'exec ip route add {taddr}/{taddr_l} via {raddr} '
503                         '{interface}\n'.format(
504                             interface=Topology.get_interface_name(
505                                 node, interface),
506                             laddr=laddr + i * addr_incr,
507                             laddr_l=raddr_range,
508                             raddr=raddr + i * addr_incr,
509                             taddr=taddr + i,
510                             taddr_l=128 if taddr.version == 6 else 32))
511                     tmp_file.write(conf)
512             vat = VatExecutor()
513             vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
514                                copy_on_execute=True)
515             os.remove(tmp_filename)
516             return
517
518         cmd1 = 'sw_interface_add_del_address'
519         args1 = dict(
520             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
521             is_add=1,
522             is_ipv6=1 if laddr.version == 6 else 0,
523             del_all=0,
524             address_length=raddr_range,
525             address=None
526         )
527         cmd2 = 'ip_route_add_del'
528         args2 = dict(
529             is_add=1,
530             is_multipath=0,
531             route=None
532         )
533         err_msg = 'Failed to configure IP addresses and IP routes on ' \
534                   'interface {ifc} on host {host}'.\
535             format(ifc=interface, host=node['host'])
536
537         with PapiSocketExecutor(node) as papi_exec:
538             for i in xrange(n_tunnels):
539                 args1['address'] = getattr(laddr + i * addr_incr, 'packed')
540                 args2['route'] = IPUtil.compose_vpp_route_structure(
541                     node,
542                     taddr + i,
543                     prefix_len=128 if taddr.version == 6 else 32,
544                     interface=interface,
545                     gateway=raddr + i * addr_incr
546                 )
547                 history = False if 1 < i < n_tunnels - 1 else True
548                 papi_exec.add(cmd1, history=history, **args1).\
549                     add(cmd2, history=history, **args2)
550             papi_exec.get_replies(err_msg)
551
552     @staticmethod
553     def vpp_ipsec_add_spd(node, spd_id):
554         """Create Security Policy Database on the VPP node.
555
556         :param node: VPP node to add SPD on.
557         :param spd_id: SPD ID.
558         :type node: dict
559         :type spd_id: int
560         """
561         cmd = 'ipsec_spd_add_del'
562         err_msg = 'Failed to add Security Policy Database on host {host}'.\
563             format(host=node['host'])
564         args = dict(
565             is_add=1,
566             spd_id=int(spd_id)
567         )
568         with PapiSocketExecutor(node) as papi_exec:
569             papi_exec.add(cmd, **args).get_reply(err_msg)
570
571     @staticmethod
572     def vpp_ipsec_spd_add_if(node, spd_id, interface):
573         """Add interface to the Security Policy Database.
574
575         :param node: VPP node.
576         :param spd_id: SPD ID to add interface on.
577         :param interface: Interface name or sw_if_index.
578         :type node: dict
579         :type spd_id: int
580         :type interface: str or int
581         """
582         cmd = 'ipsec_interface_add_del_spd'
583         err_msg = 'Failed to add interface {ifc} to Security Policy Database ' \
584                   '{spd} on host {host}'.\
585             format(ifc=interface, spd=spd_id, host=node['host'])
586         args = dict(
587             is_add=1,
588             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
589             spd_id=int(spd_id)
590         )
591         with PapiSocketExecutor(node) as papi_exec:
592             papi_exec.add(cmd, **args).get_reply(err_msg)
593
594     @staticmethod
595     def vpp_ipsec_policy_add(
596             node, spd_id, priority, action, inbound=True, sa_id=None,
597             laddr_range=None, raddr_range=None, proto=None, lport_range=None,
598             rport_range=None, is_ipv6=False):
599         """Create Security Policy Database entry on the VPP node.
600
601         :param node: VPP node to add SPD entry on.
602         :param spd_id: SPD ID to add entry on.
603         :param priority: SPD entry priority, higher number = higher priority.
604         :param action: Policy action.
605         :param inbound: If True policy is for inbound traffic, otherwise
606             outbound.
607         :param sa_id: SAD entry ID for protect action.
608         :param laddr_range: Policy selector local IPv4 or IPv6 address range in
609             format IP/prefix or IP/mask. If no mask is provided,
610             it's considered to be /32.
611         :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
612             format IP/prefix or IP/mask. If no mask is provided,
613             it's considered to be /32.
614         :param proto: Policy selector next layer protocol number.
615         :param lport_range: Policy selector local TCP/UDP port range in format
616             <port_start>-<port_end>.
617         :param rport_range: Policy selector remote TCP/UDP port range in format
618             <port_start>-<port_end>.
619         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
620             not defined so it will default to address ::/0, otherwise False.
621         :type node: dict
622         :type spd_id: int
623         :type priority: int
624         :type action: PolicyAction
625         :type inbound: bool
626         :type sa_id: int
627         :type laddr_range: string
628         :type raddr_range: string
629         :type proto: int
630         :type lport_range: string
631         :type rport_range: string
632         :type is_ipv6: bool
633         """
634
635         if laddr_range is None:
636             laddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
637
638         if raddr_range is None:
639             raddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
640
641         cmd = 'ipsec_spd_entry_add_del'
642         err_msg = 'Failed to add entry to Security Policy Database ' \
643                   '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
644
645         spd_entry = dict(
646             spd_id=int(spd_id),
647             priority=int(priority),
648             is_outbound=0 if inbound else 1,
649             sa_id=int(sa_id) if sa_id else 0,
650             policy=action.policy_int_repr,
651             protocol=int(proto) if proto else 0,
652             remote_address_start=IPUtil.create_ip_address_object(
653                 ip_network(unicode(raddr_range), strict=False).network_address),
654             remote_address_stop=IPUtil.create_ip_address_object(
655                 ip_network(
656                     unicode(raddr_range), strict=False).broadcast_address),
657             local_address_start=IPUtil.create_ip_address_object(
658                 ip_network(
659                     unicode(laddr_range), strict=False).network_address),
660             local_address_stop=IPUtil.create_ip_address_object(
661                 ip_network(
662                     unicode(laddr_range), strict=False).broadcast_address),
663             remote_port_start=int(rport_range.split('-')[0]) if rport_range
664             else 0,
665             remote_port_stop=int(rport_range.split('-')[1]) if rport_range
666             else 65535,
667             local_port_start=int(lport_range.split('-')[0]) if lport_range
668             else 0,
669             local_port_stop=int(lport_range.split('-')[1]) if rport_range
670             else 65535
671         )
672         args = dict(
673             is_add=1,
674             entry=spd_entry
675         )
676         with PapiSocketExecutor(node) as papi_exec:
677             papi_exec.add(cmd, **args).get_reply(err_msg)
678
679     @staticmethod
680     def vpp_ipsec_spd_add_entries(
681             node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip):
682         """Create multiple Security Policy Database entries on the VPP node.
683
684         :param node: VPP node to add SPD entries on.
685         :param n_entries: Number of SPD entries to be added.
686         :param spd_id: SPD ID to add entries on.
687         :param priority: SPD entries priority, higher number = higher priority.
688         :param inbound: If True policy is for inbound traffic, otherwise
689             outbound.
690         :param sa_id: SAD entry ID for first entry. Each subsequent entry will
691             SAD entry ID incremented by 1.
692         :param raddr_ip: Policy selector remote IPv4 start address for the first
693             entry. Remote IPv4 end address will be calculated depending on
694             raddr_range parameter. Each subsequent entry will have start address
695             next after IPv4 end address of previous entry.
696         :type node: dict
697         :type n_entries: int
698         :type spd_id: int
699         :type priority: int
700         :type inbound: bool
701         :type sa_id: int
702         :type raddr_ip: string
703         """
704         if int(n_entries) > 10:
705             tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.\
706                 format(sa_id)
707
708             with open(tmp_filename, 'w') as tmp_file:
709                 for i in xrange(n_entries):
710                     raddr_s = ip_address(unicode(raddr_ip)) + i
711                     raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1
712                     tunnel = (
713                         'exec ipsec policy add spd {spd_id} '
714                         'priority {priority} {direction} action protect '
715                         'sa {sa_id} remote-ip-range {raddr_s} - {raddr_e} '
716                         'local-ip-range 0.0.0.0 - 255.255.255.255\n'.
717                         format(
718                             spd_id=spd_id,
719                             priority=priority,
720                             direction='inbound' if inbound else 'outbound',
721                             sa_id=sa_id+i,
722                             raddr_s=raddr_s,
723                             raddr_e=raddr_e))
724                     tmp_file.write(tunnel)
725             VatExecutor().execute_script(
726                 tmp_filename, node, timeout=300, json_out=False,
727                 copy_on_execute=True)
728             os.remove(tmp_filename)
729             return
730
731         raddr_ip = ip_address(unicode(raddr_ip))
732         laddr_range = '::/0' if raddr_ip.version == 6 else '0.0.0.0/0'
733
734         cmd = 'ipsec_spd_entry_add_del'
735         err_msg = 'Failed to add entry to Security Policy Database ' \
736                   '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
737
738         spd_entry = dict(
739             spd_id=int(spd_id),
740             priority=int(priority),
741             is_outbound=0 if inbound else 1,
742             sa_id=int(sa_id) if sa_id else 0,
743             policy=IPsecUtil.policy_action_protect().policy_int_repr,
744             protocol=0,
745             remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
746             remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
747             local_address_start=IPUtil.create_ip_address_object(
748                 ip_network(unicode(laddr_range), strict=False).network_address),
749             local_address_stop=IPUtil.create_ip_address_object(
750                 ip_network(
751                     unicode(laddr_range), strict=False).broadcast_address),
752             remote_port_start=0,
753             remote_port_stop=65535,
754             local_port_start=0,
755             local_port_stop=65535
756         )
757         args = dict(
758             is_add=1,
759             entry=spd_entry
760         )
761
762         with PapiSocketExecutor(node) as papi_exec:
763             for i in xrange(n_entries):
764                 args['entry']['remote_address_start']['un'] = \
765                     IPUtil.union_addr(raddr_ip + i)
766                 args['entry']['remote_address_stop']['un'] = \
767                     IPUtil.union_addr(raddr_ip + i)
768                 history = False if 1 < i < n_entries - 1 else True
769                 papi_exec.add(cmd, history=history, **args)
770             papi_exec.get_replies(err_msg)
771
772     @staticmethod
773     def vpp_ipsec_create_tunnel_interfaces(
774             nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
775             crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range):
776         """Create multiple IPsec tunnel interfaces between two VPP nodes.
777
778         :param nodes: VPP nodes to create tunnel interfaces.
779         :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
780         :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
781         :param if1_key: VPP node 1 interface key from topology file.
782         :param if2_key: VPP node 2 interface key from topology file.
783         :param n_tunnels: Number of tunnel interfaces to create.
784         :param crypto_alg: The encryption algorithm name.
785         :param integ_alg: The integrity algorithm name.
786         :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
787             first tunnel in direction node1->node2.
788         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
789             first tunnel in direction node2->node1.
790         :param raddr_range: Mask specifying range of Policy selector Remote
791             IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
792             and to 128 in case of IPv6.
793         :type nodes: dict
794         :type if1_ip_addr: str
795         :type if2_ip_addr: str
796         :type if1_key: str
797         :type if2_key: str
798         :type n_tunnels: int
799         :type crypto_alg: CryptoAlg
800         :type integ_alg: IntegAlg
801         :type raddr_ip1: string
802         :type raddr_ip2: string
803         :type raddr_range: int
804         """
805         n_tunnels = int(n_tunnels)
806         spi_1 = 100000
807         spi_2 = 200000
808         if1_ip = ip_address(unicode(if1_ip_addr))
809         if2_ip = ip_address(unicode(if2_ip_addr))
810         raddr_ip1 = ip_address(unicode(raddr_ip1))
811         raddr_ip2 = ip_address(unicode(raddr_ip2))
812         addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
813             else 1 << (32 - raddr_range)
814
815         if n_tunnels > 10:
816             tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
817             tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
818             vat = VatExecutor()
819             with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
820                 tmp_f1.write(
821                     'exec create loopback interface\n'
822                     'exec set interface state loop0 up\n'
823                     'exec set interface ip address {uifc} {iaddr}/{mask}\n'
824                     .format(
825                         iaddr=if2_ip - 1,
826                         uifc=Topology.get_interface_name(
827                             nodes['DUT1'], if1_key),
828                         mask=96 if if2_ip.version == 6 else 24))
829                 tmp_f2.write(
830                     'exec set interface ip address {uifc} {iaddr}/{mask}\n'
831                     .format(
832                         iaddr=if2_ip,
833                         uifc=Topology.get_interface_name(
834                             nodes['DUT2'], if2_key),
835                         mask=96 if if2_ip.version == 6 else 24))
836                 for i in xrange(n_tunnels):
837                     ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(
838                         crypto_alg)).encode('hex')
839                     if integ_alg:
840                         ikey = gen_key(IPsecUtil.get_integ_alg_key_len(
841                             integ_alg)).encode('hex')
842                         integ = (
843                             'integ_alg {integ_alg} '
844                             'local_integ_key {local_integ_key} '
845                             'remote_integ_key {remote_integ_key} '
846                             .format(
847                                 integ_alg=integ_alg.alg_name,
848                                 local_integ_key=ikey,
849                                 remote_integ_key=ikey))
850                     else:
851                         integ = ''
852                     tmp_f1.write(
853                         'exec set interface ip address loop0 {laddr}/32\n'
854                         'ipsec_tunnel_if_add_del '
855                         'local_spi {local_spi} '
856                         'remote_spi {remote_spi} '
857                         'crypto_alg {crypto_alg} '
858                         'local_crypto_key {local_crypto_key} '
859                         'remote_crypto_key {remote_crypto_key} '
860                         '{integ} '
861                         'local_ip {laddr} '
862                         'remote_ip {raddr}\n'
863                         .format(
864                             local_spi=spi_1 + i,
865                             remote_spi=spi_2 + i,
866                             crypto_alg=crypto_alg.alg_name,
867                             local_crypto_key=ckey,
868                             remote_crypto_key=ckey,
869                             integ=integ,
870                             laddr=if1_ip + i * addr_incr,
871                             raddr=if2_ip))
872                     tmp_f2.write(
873                         'ipsec_tunnel_if_add_del '
874                         'local_spi {local_spi} '
875                         'remote_spi {remote_spi} '
876                         'crypto_alg {crypto_alg} '
877                         'local_crypto_key {local_crypto_key} '
878                         'remote_crypto_key {remote_crypto_key} '
879                         '{integ} '
880                         'local_ip {laddr} '
881                         'remote_ip {raddr}\n'
882                         .format(
883                             local_spi=spi_2 + i,
884                             remote_spi=spi_1 + i,
885                             crypto_alg=crypto_alg.alg_name,
886                             local_crypto_key=ckey,
887                             remote_crypto_key=ckey,
888                             integ=integ,
889                             laddr=if2_ip,
890                             raddr=if1_ip + i * addr_incr))
891             vat.execute_script(
892                 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
893                 copy_on_execute=True,
894                 history=False if n_tunnels > 100 else True)
895             vat.execute_script(
896                 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
897                 copy_on_execute=True,
898                 history=False if n_tunnels > 100 else True)
899             os.remove(tmp_fn1)
900             os.remove(tmp_fn2)
901
902             with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
903                 tmp_f2.write(
904                     'exec ip route add {raddr} via {uifc} {iaddr}\n'
905                     .format(
906                         raddr=ip_network(unicode(if1_ip_addr+'/8'), False),
907                         iaddr=if2_ip - 1,
908                         uifc=Topology.get_interface_name(
909                             nodes['DUT2'], if2_key)))
910                 for i in xrange(n_tunnels):
911                     tmp_f1.write(
912                         'exec set interface unnumbered ipsec{i} use {uifc}\n'
913                         'exec set interface state ipsec{i} up\n'
914                         'exec ip route add {taddr}/{mask} via ipsec{i}\n'
915                         .format(
916                             taddr=raddr_ip2 + i,
917                             i=i,
918                             uifc=Topology.get_interface_name(nodes['DUT1'],
919                                                              if1_key),
920                             mask=128 if if2_ip.version == 6 else 32))
921                     tmp_f2.write(
922                         'exec set interface unnumbered ipsec{i} use {uifc}\n'
923                         'exec set interface state ipsec{i} up\n'
924                         'exec ip route add {taddr}/{mask} via ipsec{i}\n'
925                         .format(
926                             taddr=raddr_ip1 + i,
927                             i=i,
928                             uifc=Topology.get_interface_name(nodes['DUT2'],
929                                                              if2_key),
930                             mask=128 if if2_ip.version == 6 else 32))
931             vat.execute_script(
932                 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
933                 copy_on_execute=True,
934                 history=False if n_tunnels > 100 else True)
935             vat.execute_script(
936                 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
937                 copy_on_execute=True,
938                 history=False if n_tunnels > 100 else True)
939             os.remove(tmp_fn1)
940             os.remove(tmp_fn2)
941             return
942
943         with PapiSocketExecutor(nodes['DUT1']) as papi_exec:
944             # Create loopback interface on DUT1, set it to up state
945             cmd1 = 'create_loopback'
946             args1 = dict(mac_address=0)
947             err_msg = 'Failed to create loopback interface on host {host}'.\
948                 format(host=nodes['DUT1']['host'])
949             loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
950                 get_sw_if_index(err_msg)
951             cmd1 = 'sw_interface_set_flags'
952             args1 = dict(
953                 sw_if_index=loop_sw_if_idx,
954                 admin_up_down=1)
955             err_msg = 'Failed to set loopback interface state up on host ' \
956                       '{host}'.format(host=nodes['DUT1']['host'])
957             papi_exec.add(cmd1, **args1).get_reply(err_msg)
958             # Set IP address on VPP node 1 interface
959             cmd1 = 'sw_interface_add_del_address'
960             args1 = dict(
961                 sw_if_index=InterfaceUtil.get_interface_index(
962                     nodes['DUT1'], if1_key),
963                 is_add=1,
964                 is_ipv6=1 if if2_ip.version == 6 else 0,
965                 del_all=0,
966                 address_length=96 if if2_ip.version == 6 else 24,
967                 address=getattr(if2_ip - 1, 'packed'))
968             err_msg = 'Failed to set IP address on interface {ifc} on host ' \
969                       '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host'])
970             papi_exec.add(cmd1, **args1).get_reply(err_msg)
971             # Configure IPsec tunnel interfaces
972             args1 = dict(
973                 sw_if_index=loop_sw_if_idx,
974                 is_add=1,
975                 is_ipv6=1 if if1_ip.version == 6 else 0,
976                 del_all=0,
977                 address_length=128 if if1_ip.version == 6 else 32,
978                 address='')
979             cmd2 = 'ipsec_tunnel_if_add_del'
980             args2 = dict(
981                 is_add=1,
982                 local_ip=None,
983                 remote_ip=None,
984                 local_spi=0,
985                 remote_spi=0,
986                 crypto_alg=crypto_alg.alg_int_repr,
987                 local_crypto_key_len=0,
988                 local_crypto_key=None,
989                 remote_crypto_key_len=0,
990                 remote_crypto_key=None,
991                 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
992                 local_integ_key_len=0,
993                 local_integ_key=None,
994                 remote_integ_key_len=0,
995                 remote_integ_key=None,
996                 tx_table_id=0
997             )
998             err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
999                 format(host=nodes['DUT1']['host'])
1000             ipsec_tunnels = list()
1001             ckeys = list()
1002             ikeys = list()
1003             for i in xrange(n_tunnels):
1004                 ckeys.append(
1005                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1006                 if integ_alg:
1007                     ikeys.append(
1008                         gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1009                 args1['address'] = getattr(if1_ip + i * addr_incr, 'packed')
1010                 args2['local_spi'] = spi_1 + i
1011                 args2['remote_spi'] = spi_2 + i
1012                 args2['local_ip'] = IPUtil.create_ip_address_object(
1013                     if1_ip + i * addr_incr)
1014                 args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip)
1015                 args2['local_crypto_key_len'] = len(ckeys[i])
1016                 args2['local_crypto_key'] = ckeys[i]
1017                 args2['remote_crypto_key_len'] = len(ckeys[i])
1018                 args2['remote_crypto_key'] = ckeys[i]
1019                 if integ_alg:
1020                     args2['local_integ_key_len'] = len(ikeys[i])
1021                     args2['local_integ_key'] = ikeys[i]
1022                     args2['remote_integ_key_len'] = len(ikeys[i])
1023                     args2['remote_integ_key'] = ikeys[i]
1024                 history = False if 1 < i < n_tunnels - 1 else True
1025                 papi_exec.add(cmd1, history=history, **args1).\
1026                     add(cmd2, history=history, **args2)
1027             replies = papi_exec.get_replies(err_msg)
1028             for reply in replies:
1029                 if 'sw_if_index' in reply:
1030                     ipsec_tunnels.append(reply["sw_if_index"])
1031             # Configure IP routes
1032             cmd1 = 'sw_interface_set_unnumbered'
1033             args1 = dict(
1034                 is_add=1,
1035                 sw_if_index=InterfaceUtil.get_interface_index(
1036                     nodes['DUT1'], if1_key),
1037                 unnumbered_sw_if_index=0
1038             )
1039             cmd2 = 'sw_interface_set_flags'
1040             args2 = dict(
1041                 sw_if_index=0,
1042                 admin_up_down=1)
1043             cmd3 = 'ip_route_add_del'
1044             args3 = dict(
1045                 is_add=1,
1046                 is_multipath=0,
1047                 route=None
1048             )
1049             err_msg = 'Failed to add IP routes on host {host}'.format(
1050                 host=nodes['DUT1']['host'])
1051             for i in xrange(n_tunnels):
1052                 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1053                 args2['sw_if_index'] = ipsec_tunnels[i]
1054                 args3['route'] = IPUtil.compose_vpp_route_structure(
1055                     nodes['DUT1'],
1056                     (raddr_ip2 + i).compressed,
1057                     prefix_len=128 if raddr_ip2.version == 6 else 32,
1058                     interface=ipsec_tunnels[i]
1059                 )
1060                 history = False if 1 < i < n_tunnels - 1 else True
1061                 papi_exec.add(cmd1, history=history, **args1).\
1062                     add(cmd2, history=history, **args2).\
1063                     add(cmd3, history=history, **args3)
1064             papi_exec.get_replies(err_msg)
1065
1066         with PapiSocketExecutor(nodes['DUT2']) as papi_exec:
1067             # Set IP address on VPP node 2 interface
1068             cmd1 = 'sw_interface_add_del_address'
1069             args1 = dict(
1070                 sw_if_index=InterfaceUtil.get_interface_index(
1071                     nodes['DUT2'], if2_key),
1072                 is_add=1,
1073                 is_ipv6=1 if if2_ip.version == 6 else 0,
1074                 del_all=0,
1075                 address_length=96 if if2_ip.version == 6 else 24,
1076                 address=if2_ip.packed)
1077             err_msg = 'Failed to set IP address on interface {ifc} on host ' \
1078                       '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host'])
1079             papi_exec.add(cmd1, **args1).get_reply(err_msg)
1080             # Configure IPsec tunnel interfaces
1081             cmd2 = 'ipsec_tunnel_if_add_del'
1082             args2 = dict(
1083                 is_add=1,
1084                 local_ip=IPUtil.create_ip_address_object(if2_ip),
1085                 remote_ip=None,
1086                 local_spi=0,
1087                 remote_spi=0,
1088                 crypto_alg=crypto_alg.alg_int_repr,
1089                 local_crypto_key_len=0,
1090                 local_crypto_key=None,
1091                 remote_crypto_key_len=0,
1092                 remote_crypto_key=None,
1093                 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1094                 local_integ_key_len=0,
1095                 local_integ_key=None,
1096                 remote_integ_key_len=0,
1097                 remote_integ_key=None,
1098                 tx_table_id=0
1099             )
1100             err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \
1101                 format(host=nodes['DUT2']['host'])
1102             ipsec_tunnels = list()
1103             for i in xrange(n_tunnels):
1104                 args2['local_spi'] = spi_2 + i
1105                 args2['remote_spi'] = spi_1 + i
1106                 args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip)
1107                 args2['remote_ip'] = IPUtil.create_ip_address_object(
1108                     if1_ip + i * addr_incr)
1109                 args2['local_crypto_key_len'] = len(ckeys[i])
1110                 args2['local_crypto_key'] = ckeys[i]
1111                 args2['remote_crypto_key_len'] = len(ckeys[i])
1112                 args2['remote_crypto_key'] = ckeys[i]
1113                 if integ_alg:
1114                     args2['local_integ_key_len'] = len(ikeys[i])
1115                     args2['local_integ_key'] = ikeys[i]
1116                     args2['remote_integ_key_len'] = len(ikeys[i])
1117                     args2['remote_integ_key'] = ikeys[i]
1118                 history = False if 1 < i < n_tunnels - 1 else True
1119                 papi_exec.add(cmd2, history=history, **args2)
1120             replies = papi_exec.get_replies(err_msg)
1121             for reply in replies:
1122                 if 'sw_if_index' in reply:
1123                     ipsec_tunnels.append(reply["sw_if_index"])
1124             # Configure IP routes
1125             cmd1 = 'ip_route_add_del'
1126             route = IPUtil.compose_vpp_route_structure(
1127                 nodes['DUT2'], if1_ip.compressed,
1128                 prefix_len=32 if if1_ip.version == 6 else 8,
1129                 interface=if2_key,
1130                 gateway=(if2_ip - 1).compressed
1131             )
1132             args1 = dict(
1133                 is_add=1,
1134                 is_multipath=0,
1135                 route=route
1136             )
1137             papi_exec.add(cmd1, **args1)
1138             cmd1 = 'sw_interface_set_unnumbered'
1139             args1 = dict(
1140                 is_add=1,
1141                 sw_if_index=InterfaceUtil.get_interface_index(
1142                     nodes['DUT2'], if2_key),
1143                 unnumbered_sw_if_index=0
1144             )
1145             cmd2 = 'sw_interface_set_flags'
1146             args2 = dict(
1147                 sw_if_index=0,
1148                 admin_up_down=1)
1149             cmd3 = 'ip_route_add_del'
1150             args3 = dict(
1151                 is_add=1,
1152                 is_multipath=0,
1153                 route=None
1154             )
1155             err_msg = 'Failed to add IP routes on host {host}'.format(
1156                 host=nodes['DUT2']['host'])
1157             for i in xrange(n_tunnels):
1158                 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1159                 args2['sw_if_index'] = ipsec_tunnels[i]
1160                 args3['route'] = IPUtil.compose_vpp_route_structure(
1161                     nodes['DUT1'],
1162                     (raddr_ip1 + i).compressed,
1163                     prefix_len=128 if raddr_ip1.version == 6 else 32,
1164                     interface=ipsec_tunnels[i]
1165                 )
1166                 history = False if 1 < i < n_tunnels - 1 else True
1167                 papi_exec.add(cmd1, history=history, **args1). \
1168                     add(cmd2, history=history, **args2). \
1169                     add(cmd3, history=history, **args3)
1170             papi_exec.get_replies(err_msg)
1171
1172     @staticmethod
1173     def vpp_ipsec_add_multiple_tunnels(
1174             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1175             tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1176         """Create multiple IPsec tunnels between two VPP nodes.
1177
1178         :param nodes: VPP nodes to create tunnels.
1179         :param interface1: Interface name or sw_if_index on node 1.
1180         :param interface2: Interface name or sw_if_index on node 2.
1181         :param n_tunnels: Number of tunnels to create.
1182         :param crypto_alg: The encryption algorithm name.
1183         :param integ_alg: The integrity algorithm name.
1184         :param tunnel_ip1: Tunnel node1 IPv4 address.
1185         :param tunnel_ip2: Tunnel node2 IPv4 address.
1186         :param raddr_ip1: Policy selector remote IPv4 start address for the
1187             first tunnel in direction node1->node2.
1188         :param raddr_ip2: Policy selector remote IPv4 start address for the
1189             first tunnel in direction node2->node1.
1190         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1191             addresses. Valid values are from 1 to 32.
1192         :type nodes: dict
1193         :type interface1: str or int
1194         :type interface2: str or int
1195         :type n_tunnels: int
1196         :type crypto_alg: CryptoAlg
1197         :type integ_alg: IntegAlg
1198         :type tunnel_ip1: str
1199         :type tunnel_ip2: str
1200         :type raddr_ip1: string
1201         :type raddr_ip2: string
1202         :type raddr_range: int
1203         """
1204         spd_id = 1
1205         p_hi = 100
1206         p_lo = 10
1207         sa_id_1 = 100000
1208         sa_id_2 = 200000
1209         spi_1 = 300000
1210         spi_2 = 400000
1211
1212         crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1213         integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \
1214             if integ_alg else ''
1215
1216         IPsecUtil.vpp_ipsec_set_ip_route(
1217             nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1218             interface1, raddr_range)
1219         IPsecUtil.vpp_ipsec_set_ip_route(
1220             nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1221             interface2, raddr_range)
1222
1223         IPsecUtil.vpp_ipsec_add_spd(
1224             nodes['DUT1'], spd_id)
1225         IPsecUtil.vpp_ipsec_spd_add_if(
1226             nodes['DUT1'], spd_id, interface1)
1227         IPsecUtil.vpp_ipsec_policy_add(
1228             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1229             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1230         IPsecUtil.vpp_ipsec_policy_add(
1231             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1232             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1233
1234         IPsecUtil.vpp_ipsec_add_spd(
1235             nodes['DUT2'], spd_id)
1236         IPsecUtil.vpp_ipsec_spd_add_if(
1237             nodes['DUT2'], spd_id, interface2)
1238         IPsecUtil.vpp_ipsec_policy_add(
1239             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1240             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1241         IPsecUtil.vpp_ipsec_policy_add(
1242             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1243             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1244
1245         IPsecUtil.vpp_ipsec_add_sad_entries(
1246             nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1247             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1248
1249         IPsecUtil.vpp_ipsec_spd_add_entries(
1250             nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
1251
1252         IPsecUtil.vpp_ipsec_add_sad_entries(
1253             nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1254             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1255
1256         IPsecUtil.vpp_ipsec_spd_add_entries(
1257             nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
1258
1259         IPsecUtil.vpp_ipsec_add_sad_entries(
1260             nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1261             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1262
1263         IPsecUtil.vpp_ipsec_spd_add_entries(
1264             nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
1265
1266         IPsecUtil.vpp_ipsec_add_sad_entries(
1267             nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1268             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1269
1270         IPsecUtil.vpp_ipsec_spd_add_entries(
1271             nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
1272
1273     @staticmethod
1274     def vpp_ipsec_show(node):
1275         """Run "show ipsec" debug CLI command.
1276
1277         :param node: Node to run command on.
1278         :type node: dict
1279         """
1280         PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')