FIX: Ipsec perf
[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     InterfaceStatusFlags
27 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
28 from resources.libraries.python.topology import Topology
29 from resources.libraries.python.VatExecutor import VatExecutor
30
31
32 def gen_key(length):
33     """Generate random string as a key.
34
35     :param length: Length of generated payload.
36     :type length: int
37     :returns: The generated payload.
38     :rtype: str
39     """
40     return ''.join(choice(letters) for _ in range(length))
41
42
43 class PolicyAction(Enum):
44     """Policy actions."""
45     BYPASS = ('bypass', 0)
46     DISCARD = ('discard', 1)
47     PROTECT = ('protect', 3)
48
49     def __init__(self, policy_name, policy_int_repr):
50         self.policy_name = policy_name
51         self.policy_int_repr = policy_int_repr
52
53
54 class CryptoAlg(Enum):
55     """Encryption algorithms."""
56     AES_CBC_128 = ('aes-cbc-128', 1, 'AES-CBC', 16)
57     AES_CBC_256 = ('aes-cbc-256', 3, 'AES-CBC', 32)
58     AES_GCM_128 = ('aes-gcm-128', 7, 'AES-GCM', 16)
59     AES_GCM_256 = ('aes-gcm-256', 9, 'AES-GCM', 32)
60
61     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
62         self.alg_name = alg_name
63         self.alg_int_repr = alg_int_repr
64         self.scapy_name = scapy_name
65         self.key_len = key_len
66
67
68 class IntegAlg(Enum):
69     """Integrity algorithm."""
70     SHA_256_128 = ('sha-256-128', 4, 'SHA2-256-128', 32)
71     SHA_512_256 = ('sha-512-256', 6, 'SHA2-512-256', 64)
72
73     def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
74         self.alg_name = alg_name
75         self.alg_int_repr = alg_int_repr
76         self.scapy_name = scapy_name
77         self.key_len = key_len
78
79
80 class IPsecProto(IntEnum):
81     """IPsec protocol."""
82     ESP = 1
83     SEC_AH = 0
84
85
86 class IPsecSadFlags(IntEnum):
87     """IPsec Security Association Database flags."""
88     IPSEC_API_SAD_FLAG_NONE = 0
89     IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
90     IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
91
92
93 class IPsecUtil(object):
94     """IPsec utilities."""
95
96     @staticmethod
97     def policy_action_bypass():
98         """Return policy action bypass.
99
100         :returns: PolicyAction enum BYPASS object.
101         :rtype: PolicyAction
102         """
103         return PolicyAction.BYPASS
104
105     @staticmethod
106     def policy_action_discard():
107         """Return policy action discard.
108
109         :returns: PolicyAction enum DISCARD object.
110         :rtype: PolicyAction
111         """
112         return PolicyAction.DISCARD
113
114     @staticmethod
115     def policy_action_protect():
116         """Return policy action protect.
117
118         :returns: PolicyAction enum PROTECT object.
119         :rtype: PolicyAction
120         """
121         return PolicyAction.PROTECT
122
123     @staticmethod
124     def crypto_alg_aes_cbc_128():
125         """Return encryption algorithm aes-cbc-128.
126
127         :returns: CryptoAlg enum AES_CBC_128 object.
128         :rtype: CryptoAlg
129         """
130         return CryptoAlg.AES_CBC_128
131
132     @staticmethod
133     def crypto_alg_aes_cbc_256():
134         """Return encryption algorithm aes-cbc-256.
135
136         :returns: CryptoAlg enum AES_CBC_256 object.
137         :rtype: CryptoAlg
138         """
139         return CryptoAlg.AES_CBC_256
140
141     @staticmethod
142     def crypto_alg_aes_gcm_128():
143         """Return encryption algorithm aes-gcm-128.
144
145         :returns: CryptoAlg enum AES_GCM_128 object.
146         :rtype: CryptoAlg
147         """
148         return CryptoAlg.AES_GCM_128
149
150     @staticmethod
151     def crypto_alg_aes_gcm_256():
152         """Return encryption algorithm aes-gcm-256.
153
154         :returns: CryptoAlg enum AES_GCM_128 object.
155         :rtype: CryptoAlg
156         """
157         return CryptoAlg.AES_GCM_256
158
159     @staticmethod
160     def get_crypto_alg_key_len(crypto_alg):
161         """Return encryption algorithm key length.
162
163         :param crypto_alg: Encryption algorithm.
164         :type crypto_alg: CryptoAlg
165         :returns: Key length.
166         :rtype: int
167         """
168         return crypto_alg.key_len
169
170     @staticmethod
171     def get_crypto_alg_scapy_name(crypto_alg):
172         """Return encryption algorithm scapy name.
173
174         :param crypto_alg: Encryption algorithm.
175         :type crypto_alg: CryptoAlg
176         :returns: Algorithm scapy name.
177         :rtype: str
178         """
179         return crypto_alg.scapy_name
180
181     @staticmethod
182     def integ_alg_sha_256_128():
183         """Return integrity algorithm SHA-256-128.
184
185         :returns: IntegAlg enum SHA_256_128 object.
186         :rtype: IntegAlg
187         """
188         return IntegAlg.SHA_256_128
189
190     @staticmethod
191     def integ_alg_sha_512_256():
192         """Return integrity algorithm SHA-512-256.
193
194         :returns: IntegAlg enum SHA_512_256 object.
195         :rtype: IntegAlg
196         """
197         return IntegAlg.SHA_512_256
198
199     @staticmethod
200     def get_integ_alg_key_len(integ_alg):
201         """Return integrity algorithm key length.
202
203         :param integ_alg: Integrity algorithm.
204         :type integ_alg: IntegAlg
205         :returns: Key length.
206         :rtype: int
207         """
208         return integ_alg.key_len
209
210     @staticmethod
211     def get_integ_alg_scapy_name(integ_alg):
212         """Return integrity algorithm scapy name.
213
214         :param integ_alg: Integrity algorithm.
215         :type integ_alg: IntegAlg
216         :returns: Algorithm scapy name.
217         :rtype: str
218         """
219         return integ_alg.scapy_name
220
221     @staticmethod
222     def ipsec_proto_esp():
223         """Return IPSec protocol ESP.
224
225         :returns: IPsecProto enum ESP object.
226         :rtype: IPsecProto
227         """
228         return int(IPsecProto.ESP)
229
230     @staticmethod
231     def ipsec_proto_ah():
232         """Return IPSec protocol AH.
233
234         :returns: IPsecProto enum AH object.
235         :rtype: IPsecProto
236         """
237         return int(IPsecProto.SEC_AH)
238
239     @staticmethod
240     def vpp_ipsec_select_backend(node, protocol, index=1):
241         """Select IPsec backend.
242
243         :param node: VPP node to select IPsec backend on.
244         :param protocol: IPsec protocol.
245         :param index: Backend index.
246         :type node: dict
247         :type protocol: IPsecProto
248         :type index: int
249         :raises RuntimeError: If failed to select IPsec backend or if no API
250             reply received.
251         """
252         cmd = 'ipsec_select_backend'
253         err_msg = 'Failed to select IPsec backend on host {host}'.format(
254             host=node['host'])
255         args = dict(
256             protocol=protocol,
257             index=index
258         )
259         with PapiSocketExecutor(node) as papi_exec:
260             papi_exec.add(cmd, **args).get_reply(err_msg)
261
262     @staticmethod
263     def vpp_ipsec_backend_dump(node):
264         """Dump IPsec backends.
265
266         :param node: VPP node to dump IPsec backend on.
267         :type node: dict
268         """
269         err_msg = 'Failed to dump IPsec backends on host {host}'.format(
270             host=node['host'])
271         with PapiSocketExecutor(node) as papi_exec:
272             papi_exec.add('ipsec_backend_dump').get_details(err_msg)
273
274     @staticmethod
275     def vpp_ipsec_add_sad_entry(
276             node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
277             integ_key='', tunnel_src=None, tunnel_dst=None):
278         """Create Security Association Database entry on the VPP node.
279
280         :param node: VPP node to add SAD entry on.
281         :param sad_id: SAD entry ID.
282         :param spi: Security Parameter Index of this SAD entry.
283         :param crypto_alg: The encryption algorithm name.
284         :param crypto_key: The encryption key string.
285         :param integ_alg: The integrity algorithm name.
286         :param integ_key: The integrity key string.
287         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
288             specified ESP transport mode is used.
289         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
290             not specified ESP transport mode is used.
291         :type node: dict
292         :type sad_id: int
293         :type spi: int
294         :type crypto_alg: CryptoAlg
295         :type crypto_key: str
296         :type integ_alg: IntegAlg
297         :type integ_key: str
298         :type tunnel_src: str
299         :type tunnel_dst: str
300         """
301         ckey = dict(
302             length=len(crypto_key),
303             data=crypto_key
304         )
305         ikey = dict(
306             length=len(integ_key),
307             data=integ_key if integ_key else 0
308         )
309
310         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
311         if tunnel_src and tunnel_dst:
312             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
313             src_addr = ip_address(unicode(tunnel_src))
314             dst_addr = ip_address(unicode(tunnel_dst))
315             if src_addr.version == 6:
316                 flags = \
317                     flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
318         else:
319             src_addr = ''
320             dst_addr = ''
321
322         cmd = 'ipsec_sad_entry_add_del'
323         err_msg = 'Failed to add Security Association Database entry on ' \
324                   'host {host}'.format(host=node['host'])
325         sad_entry = dict(
326             sad_id=int(sad_id),
327             spi=int(spi),
328             crypto_algorithm=crypto_alg.alg_int_repr,
329             crypto_key=ckey,
330             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
331             integrity_key=ikey,
332             flags=flags,
333             tunnel_src=str(src_addr),
334             tunnel_dst=str(dst_addr),
335             protocol=int(IPsecProto.ESP)
336         )
337         args = dict(
338             is_add=1,
339             entry=sad_entry
340         )
341         with PapiSocketExecutor(node) as papi_exec:
342             papi_exec.add(cmd, **args).get_reply(err_msg)
343
344     @staticmethod
345     def vpp_ipsec_add_sad_entries(
346             node, n_entries, sad_id, spi, crypto_alg, crypto_key,
347             integ_alg=None, integ_key='', tunnel_src=None, tunnel_dst=None):
348         """Create multiple Security Association Database entries on VPP node.
349
350         :param node: VPP node to add SAD entry on.
351         :param n_entries: Number of SAD entries to be created.
352         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
353             id incremented by 1.
354         :param spi: Security Parameter Index of first SAD entry. All subsequent
355             SAD entries will have spi incremented by 1.
356         :param crypto_alg: The encryption algorithm name.
357         :param crypto_key: The encryption key string.
358         :param integ_alg: The integrity algorithm name.
359         :param integ_key: The integrity key string.
360         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
361             specified ESP transport mode is used.
362         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
363             not specified ESP transport mode is used.
364         :type node: dict
365         :type n_entries: int
366         :type sad_id: int
367         :type spi: int
368         :type crypto_alg: CryptoAlg
369         :type crypto_key: str
370         :type integ_alg: IntegAlg
371         :type integ_key: str
372         :type tunnel_src: str
373         :type tunnel_dst: str
374         """
375         if tunnel_src and tunnel_dst:
376             src_addr = ip_address(unicode(tunnel_src))
377             dst_addr = ip_address(unicode(tunnel_dst))
378         else:
379             src_addr = ''
380             dst_addr = ''
381
382         addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
383             else 1 << (32 - 24)
384
385         if int(n_entries) > 10:
386             tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.\
387                 format(sad_id)
388
389             with open(tmp_filename, 'w') as tmp_file:
390                 for i in xrange(n_entries):
391                     integ = (
392                         'integ-alg {integ_alg} integ-key {integ_key}'.format(
393                             integ_alg=integ_alg.alg_name,
394                             integ_key=integ_key.encode('hex'))
395                         if integ_alg else '')
396                     tunnel = (
397                         'tunnel-src {laddr} tunnel-dst {raddr}'.format(
398                             laddr=src_addr + i * addr_incr,
399                             raddr=dst_addr + i * addr_incr)
400                         if tunnel_src and tunnel_dst else '')
401                     conf = (
402                         'exec ipsec sa add {sad_id} esp spi {spi} '
403                         'crypto-alg {crypto_alg} crypto-key {crypto_key} '
404                         '{integ} {tunnel}\n'.format(
405                             sad_id=sad_id + i,
406                             spi=spi + i,
407                             crypto_alg=crypto_alg.alg_name,
408                             crypto_key=crypto_key.encode('hex'),
409                             integ=integ,
410                             tunnel=tunnel))
411                     tmp_file.write(conf)
412             vat = VatExecutor()
413             vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
414                                copy_on_execute=True)
415             os.remove(tmp_filename)
416             return
417
418         ckey = dict(
419             length=len(crypto_key),
420             data=crypto_key
421         )
422         ikey = dict(
423             length=len(integ_key),
424             data=integ_key if integ_key else 0
425         )
426
427         flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
428         if tunnel_src and tunnel_dst:
429             flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
430             if src_addr.version == 6:
431                 flags = flags | int(
432                     IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
433
434         cmd = 'ipsec_sad_entry_add_del'
435         err_msg = 'Failed to add Security Association Database entry on ' \
436                   'host {host}'.format(host=node['host'])
437
438         sad_entry = dict(
439             sad_id=int(sad_id),
440             spi=int(spi),
441             crypto_algorithm=crypto_alg.alg_int_repr,
442             crypto_key=ckey,
443             integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
444             integrity_key=ikey,
445             flags=flags,
446             tunnel_src=str(src_addr),
447             tunnel_dst=str(dst_addr),
448             protocol=int(IPsecProto.ESP)
449         )
450         args = dict(
451             is_add=1,
452             entry=sad_entry
453         )
454         with PapiSocketExecutor(node) as papi_exec:
455             for i in xrange(n_entries):
456                 args['entry']['sad_id'] = int(sad_id) + i
457                 args['entry']['spi'] = int(spi) + i
458                 args['entry']['tunnel_src'] = str(src_addr + i * addr_incr) \
459                     if tunnel_src and tunnel_dst else src_addr
460                 args['entry']['tunnel_dst'] = str(dst_addr + i * addr_incr) \
461                     if tunnel_src and tunnel_dst else dst_addr
462                 history = False if 1 < i < n_entries - 1 else True
463                 papi_exec.add(cmd, history=history, **args)
464             papi_exec.get_replies(err_msg)
465
466     @staticmethod
467     def vpp_ipsec_set_ip_route(
468             node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
469             raddr_range):
470         """Set IP address and route on interface.
471
472         :param node: VPP node to add config on.
473         :param n_tunnels: Number of tunnels to create.
474         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
475         :param traffic_addr: Traffic destination IP address to route.
476         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
477         :param interface: Interface key on node 1.
478         :param raddr_range: Mask specifying range of Policy selector Remote IP
479             addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
480             in case of IPv6.
481         :type node: dict
482         :type n_tunnels: int
483         :type tunnel_src: str
484         :type traffic_addr: str
485         :type tunnel_dst: str
486         :type interface: str
487         :type raddr_range: int
488         """
489         laddr = ip_address(unicode(tunnel_src))
490         raddr = ip_address(unicode(tunnel_dst))
491         taddr = ip_address(unicode(traffic_addr))
492         addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
493             else 1 << (32 - raddr_range)
494
495         if int(n_tunnels) > 10:
496             tmp_filename = '/tmp/ipsec_set_ip.script'
497
498             with open(tmp_filename, 'w') as tmp_file:
499                 for i in xrange(n_tunnels):
500                     conf = (
501                         'exec set interface ip address {interface} '
502                         '{laddr}/{laddr_l}\n'
503                         'exec ip route add {taddr}/{taddr_l} via {raddr} '
504                         '{interface}\n'.format(
505                             interface=Topology.get_interface_name(
506                                 node, interface),
507                             laddr=laddr + i * addr_incr,
508                             laddr_l=raddr_range,
509                             raddr=raddr + i * addr_incr,
510                             taddr=taddr + i,
511                             taddr_l=128 if taddr.version == 6 else 32))
512                     tmp_file.write(conf)
513             vat = VatExecutor()
514             vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
515                                copy_on_execute=True)
516             os.remove(tmp_filename)
517             return
518
519         cmd1 = 'sw_interface_add_del_address'
520         args1 = dict(
521             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
522             is_add=True,
523             del_all=False,
524             prefix=None
525         )
526         cmd2 = 'ip_route_add_del'
527         args2 = dict(
528             is_add=1,
529             is_multipath=0,
530             route=None
531         )
532         err_msg = 'Failed to configure IP addresses and IP routes on ' \
533                   'interface {ifc} on host {host}'.\
534             format(ifc=interface, host=node['host'])
535
536         with PapiSocketExecutor(node) as papi_exec:
537             for i in xrange(n_tunnels):
538                 args1['prefix'] = IPUtil.create_prefix_object(
539                     laddr + i * addr_incr, raddr_range)
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                     'exec set ip arp {uifc} {raddr}/32 {rmac} static\n'
825                     .format(
826                         iaddr=if2_ip - 1,
827                         raddr=if2_ip,
828                         uifc=Topology.get_interface_name(
829                             nodes['DUT1'], if1_key),
830                         rmac=Topology.get_interface_mac(
831                             nodes['DUT2'], if2_key),
832                         mask=96 if if2_ip.version == 6 else 24))
833                 tmp_f2.write(
834                     'exec set interface ip address {uifc} {iaddr}/{mask}\n'
835                     .format(
836                         iaddr=if2_ip,
837                         uifc=Topology.get_interface_name(
838                             nodes['DUT2'], if2_key),
839                         mask=96 if if2_ip.version == 6 else 24))
840                 for i in xrange(n_tunnels):
841                     ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(
842                         crypto_alg)).encode('hex')
843                     if integ_alg:
844                         ikey = gen_key(IPsecUtil.get_integ_alg_key_len(
845                             integ_alg)).encode('hex')
846                         integ = (
847                             'integ_alg {integ_alg} '
848                             'local_integ_key {local_integ_key} '
849                             'remote_integ_key {remote_integ_key} '
850                             .format(
851                                 integ_alg=integ_alg.alg_name,
852                                 local_integ_key=ikey,
853                                 remote_integ_key=ikey))
854                     else:
855                         integ = ''
856                     tmp_f1.write(
857                         'exec set interface ip address loop0 {laddr}/32\n'
858                         'ipsec_tunnel_if_add_del '
859                         'local_spi {local_spi} '
860                         'remote_spi {remote_spi} '
861                         'crypto_alg {crypto_alg} '
862                         'local_crypto_key {local_crypto_key} '
863                         'remote_crypto_key {remote_crypto_key} '
864                         '{integ} '
865                         'local_ip {laddr} '
866                         'remote_ip {raddr}\n'
867                         .format(
868                             local_spi=spi_1 + i,
869                             remote_spi=spi_2 + i,
870                             crypto_alg=crypto_alg.alg_name,
871                             local_crypto_key=ckey,
872                             remote_crypto_key=ckey,
873                             integ=integ,
874                             laddr=if1_ip + i * addr_incr,
875                             raddr=if2_ip))
876                     tmp_f2.write(
877                         'ipsec_tunnel_if_add_del '
878                         'local_spi {local_spi} '
879                         'remote_spi {remote_spi} '
880                         'crypto_alg {crypto_alg} '
881                         'local_crypto_key {local_crypto_key} '
882                         'remote_crypto_key {remote_crypto_key} '
883                         '{integ} '
884                         'local_ip {laddr} '
885                         'remote_ip {raddr}\n'
886                         .format(
887                             local_spi=spi_2 + i,
888                             remote_spi=spi_1 + i,
889                             crypto_alg=crypto_alg.alg_name,
890                             local_crypto_key=ckey,
891                             remote_crypto_key=ckey,
892                             integ=integ,
893                             laddr=if2_ip,
894                             raddr=if1_ip + i * addr_incr))
895             vat.execute_script(
896                 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
897                 copy_on_execute=True,
898                 history=False if n_tunnels > 100 else True)
899             vat.execute_script(
900                 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
901                 copy_on_execute=True,
902                 history=False if n_tunnels > 100 else True)
903             os.remove(tmp_fn1)
904             os.remove(tmp_fn2)
905
906             with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
907                 tmp_f2.write(
908                     'exec ip route add {raddr} via {uifc} {iaddr}\n'
909                     .format(
910                         raddr=ip_network(unicode(if1_ip_addr+'/8'), False),
911                         iaddr=if2_ip - 1,
912                         uifc=Topology.get_interface_name(
913                             nodes['DUT2'], if2_key)))
914                 for i in xrange(n_tunnels):
915                     tmp_f1.write(
916                         'exec set interface unnumbered ipsec{i} use {uifc}\n'
917                         'exec set interface state ipsec{i} up\n'
918                         'exec ip route add {taddr}/{mask} via ipsec{i}\n'
919                         .format(
920                             taddr=raddr_ip2 + i,
921                             i=i,
922                             uifc=Topology.get_interface_name(nodes['DUT1'],
923                                                              if1_key),
924                             mask=128 if if2_ip.version == 6 else 32))
925                     tmp_f2.write(
926                         'exec set interface unnumbered ipsec{i} use {uifc}\n'
927                         'exec set interface state ipsec{i} up\n'
928                         'exec ip route add {taddr}/{mask} via ipsec{i}\n'
929                         .format(
930                             taddr=raddr_ip1 + i,
931                             i=i,
932                             uifc=Topology.get_interface_name(nodes['DUT2'],
933                                                              if2_key),
934                             mask=128 if if2_ip.version == 6 else 32))
935             vat.execute_script(
936                 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
937                 copy_on_execute=True,
938                 history=False if n_tunnels > 100 else True)
939             vat.execute_script(
940                 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
941                 copy_on_execute=True,
942                 history=False if n_tunnels > 100 else True)
943             os.remove(tmp_fn1)
944             os.remove(tmp_fn2)
945             return
946
947         with PapiSocketExecutor(nodes['DUT1']) as papi_exec:
948             # Create loopback interface on DUT1, set it to up state
949             cmd1 = 'create_loopback'
950             args1 = dict(mac_address=0)
951             err_msg = 'Failed to create loopback interface on host {host}'.\
952                 format(host=nodes['DUT1']['host'])
953             loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
954                 get_sw_if_index(err_msg)
955             cmd1 = 'sw_interface_set_flags'
956             args1 = dict(
957                 sw_if_index=loop_sw_if_idx,
958                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
959             err_msg = 'Failed to set loopback interface state up on host ' \
960                       '{host}'.format(host=nodes['DUT1']['host'])
961             papi_exec.add(cmd1, **args1).get_reply(err_msg)
962             # Set IP address on VPP node 1 interface
963             cmd1 = 'sw_interface_add_del_address'
964             args1 = dict(
965                 sw_if_index=InterfaceUtil.get_interface_index(
966                     nodes['DUT1'], if1_key),
967                 is_add=True,
968                 del_all=False,
969                 prefix=IPUtil.create_prefix_object(
970                     if2_ip - 1, 96 if if2_ip.version == 6 else 24)
971             )
972             err_msg = 'Failed to set IP address on interface {ifc} on host ' \
973                       '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host'])
974             papi_exec.add(cmd1, **args1).get_reply(err_msg)
975             cmd4 = 'ip_neighbor_add_del'
976             args4 = dict(
977                 is_add=1,
978                 neighbor=dict(
979                     sw_if_index=Topology.get_interface_sw_index(
980                         nodes['DUT1'], if1_key),
981                     flags=1,
982                     mac_address=str(
983                         Topology.get_interface_mac(nodes['DUT2'], if2_key)),
984                     ip_address=str(ip_address(unicode(if2_ip_addr)))
985                 )
986             )
987             err_msg = 'Failed to add IP neighbor on interface {ifc}'.format(
988                 ifc=if1_key)
989             papi_exec.add(cmd4, **args4).get_reply(err_msg)
990             # Configure IPsec tunnel interfaces
991             args1 = dict(
992                 sw_if_index=loop_sw_if_idx,
993                 is_add=True,
994                 del_all=False,
995                 prefix=None
996             )
997             cmd2 = 'ipsec_tunnel_if_add_del'
998             args2 = dict(
999                 is_add=1,
1000                 local_ip=None,
1001                 remote_ip=None,
1002                 local_spi=0,
1003                 remote_spi=0,
1004                 crypto_alg=crypto_alg.alg_int_repr,
1005                 local_crypto_key_len=0,
1006                 local_crypto_key=None,
1007                 remote_crypto_key_len=0,
1008                 remote_crypto_key=None,
1009                 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1010                 local_integ_key_len=0,
1011                 local_integ_key=None,
1012                 remote_integ_key_len=0,
1013                 remote_integ_key=None,
1014                 tx_table_id=0
1015             )
1016             err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
1017                 format(host=nodes['DUT1']['host'])
1018             ipsec_tunnels = list()
1019             ckeys = list()
1020             ikeys = list()
1021             for i in xrange(n_tunnels):
1022                 ckeys.append(
1023                     gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1024                 if integ_alg:
1025                     ikeys.append(
1026                         gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1027                 args1['prefix'] = IPUtil.create_prefix_object(
1028                     if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32)
1029                 args2['local_spi'] = spi_1 + i
1030                 args2['remote_spi'] = spi_2 + i
1031                 args2['local_ip'] = IPUtil.create_ip_address_object(
1032                     if1_ip + i * addr_incr)
1033                 args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip)
1034                 args2['local_crypto_key_len'] = len(ckeys[i])
1035                 args2['local_crypto_key'] = ckeys[i]
1036                 args2['remote_crypto_key_len'] = len(ckeys[i])
1037                 args2['remote_crypto_key'] = ckeys[i]
1038                 if integ_alg:
1039                     args2['local_integ_key_len'] = len(ikeys[i])
1040                     args2['local_integ_key'] = ikeys[i]
1041                     args2['remote_integ_key_len'] = len(ikeys[i])
1042                     args2['remote_integ_key'] = ikeys[i]
1043                 history = False if 1 < i < n_tunnels - 1 else True
1044                 papi_exec.add(cmd1, history=history, **args1).\
1045                     add(cmd2, history=history, **args2)
1046             replies = papi_exec.get_replies(err_msg)
1047             for reply in replies:
1048                 if 'sw_if_index' in reply:
1049                     ipsec_tunnels.append(reply["sw_if_index"])
1050             # Configure IP routes
1051             cmd1 = 'sw_interface_set_unnumbered'
1052             args1 = dict(
1053                 is_add=True,
1054                 sw_if_index=InterfaceUtil.get_interface_index(
1055                     nodes['DUT1'], if1_key),
1056                 unnumbered_sw_if_index=0
1057             )
1058             cmd2 = 'sw_interface_set_flags'
1059             args2 = dict(
1060                 sw_if_index=0,
1061                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
1062             cmd3 = 'ip_route_add_del'
1063             args3 = dict(
1064                 is_add=1,
1065                 is_multipath=0,
1066                 route=None
1067             )
1068             err_msg = 'Failed to add IP routes on host {host}'.format(
1069                 host=nodes['DUT1']['host'])
1070             for i in xrange(n_tunnels):
1071                 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1072                 args2['sw_if_index'] = ipsec_tunnels[i]
1073                 args3['route'] = IPUtil.compose_vpp_route_structure(
1074                     nodes['DUT1'],
1075                     (raddr_ip2 + i).compressed,
1076                     prefix_len=128 if raddr_ip2.version == 6 else 32,
1077                     interface=ipsec_tunnels[i]
1078                 )
1079                 history = False if 1 < i < n_tunnels - 1 else True
1080                 papi_exec.add(cmd1, history=history, **args1).\
1081                     add(cmd2, history=history, **args2).\
1082                     add(cmd3, history=history, **args3)
1083             papi_exec.get_replies(err_msg)
1084
1085         with PapiSocketExecutor(nodes['DUT2']) as papi_exec:
1086             # Set IP address on VPP node 2 interface
1087             cmd1 = 'sw_interface_add_del_address'
1088             args1 = dict(
1089                 sw_if_index=InterfaceUtil.get_interface_index(
1090                     nodes['DUT2'], if2_key),
1091                 is_add=True,
1092                 del_all=False,
1093                 prefix=IPUtil.create_prefix_object(
1094                     if2_ip, 96 if if2_ip.version == 6 else 24)
1095             )
1096             err_msg = 'Failed to set IP address on interface {ifc} on host ' \
1097                       '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host'])
1098             papi_exec.add(cmd1, **args1).get_reply(err_msg)
1099             # Configure IPsec tunnel interfaces
1100             cmd2 = 'ipsec_tunnel_if_add_del'
1101             args2 = dict(
1102                 is_add=1,
1103                 local_ip=IPUtil.create_ip_address_object(if2_ip),
1104                 remote_ip=None,
1105                 local_spi=0,
1106                 remote_spi=0,
1107                 crypto_alg=crypto_alg.alg_int_repr,
1108                 local_crypto_key_len=0,
1109                 local_crypto_key=None,
1110                 remote_crypto_key_len=0,
1111                 remote_crypto_key=None,
1112                 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1113                 local_integ_key_len=0,
1114                 local_integ_key=None,
1115                 remote_integ_key_len=0,
1116                 remote_integ_key=None,
1117                 tx_table_id=0
1118             )
1119             err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \
1120                 format(host=nodes['DUT2']['host'])
1121             ipsec_tunnels = list()
1122             for i in xrange(n_tunnels):
1123                 args2['local_spi'] = spi_2 + i
1124                 args2['remote_spi'] = spi_1 + i
1125                 args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip)
1126                 args2['remote_ip'] = IPUtil.create_ip_address_object(
1127                     if1_ip + i * addr_incr)
1128                 args2['local_crypto_key_len'] = len(ckeys[i])
1129                 args2['local_crypto_key'] = ckeys[i]
1130                 args2['remote_crypto_key_len'] = len(ckeys[i])
1131                 args2['remote_crypto_key'] = ckeys[i]
1132                 if integ_alg:
1133                     args2['local_integ_key_len'] = len(ikeys[i])
1134                     args2['local_integ_key'] = ikeys[i]
1135                     args2['remote_integ_key_len'] = len(ikeys[i])
1136                     args2['remote_integ_key'] = ikeys[i]
1137                 history = False if 1 < i < n_tunnels - 1 else True
1138                 papi_exec.add(cmd2, history=history, **args2)
1139             replies = papi_exec.get_replies(err_msg)
1140             for reply in replies:
1141                 if 'sw_if_index' in reply:
1142                     ipsec_tunnels.append(reply["sw_if_index"])
1143             # Configure IP routes
1144             cmd1 = 'ip_route_add_del'
1145             route = IPUtil.compose_vpp_route_structure(
1146                 nodes['DUT2'], if1_ip.compressed,
1147                 prefix_len=32 if if1_ip.version == 6 else 8,
1148                 interface=if2_key,
1149                 gateway=(if2_ip - 1).compressed
1150             )
1151             args1 = dict(
1152                 is_add=1,
1153                 is_multipath=0,
1154                 route=route
1155             )
1156             papi_exec.add(cmd1, **args1)
1157             cmd1 = 'sw_interface_set_unnumbered'
1158             args1 = dict(
1159                 is_add=True,
1160                 sw_if_index=InterfaceUtil.get_interface_index(
1161                     nodes['DUT2'], if2_key),
1162                 unnumbered_sw_if_index=0
1163             )
1164             cmd2 = 'sw_interface_set_flags'
1165             args2 = dict(
1166                 sw_if_index=0,
1167                 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
1168             cmd3 = 'ip_route_add_del'
1169             args3 = dict(
1170                 is_add=1,
1171                 is_multipath=0,
1172                 route=None
1173             )
1174             err_msg = 'Failed to add IP routes on host {host}'.format(
1175                 host=nodes['DUT2']['host'])
1176             for i in xrange(n_tunnels):
1177                 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1178                 args2['sw_if_index'] = ipsec_tunnels[i]
1179                 args3['route'] = IPUtil.compose_vpp_route_structure(
1180                     nodes['DUT1'],
1181                     (raddr_ip1 + i).compressed,
1182                     prefix_len=128 if raddr_ip1.version == 6 else 32,
1183                     interface=ipsec_tunnels[i]
1184                 )
1185                 history = False if 1 < i < n_tunnels - 1 else True
1186                 papi_exec.add(cmd1, history=history, **args1). \
1187                     add(cmd2, history=history, **args2). \
1188                     add(cmd3, history=history, **args3)
1189             papi_exec.get_replies(err_msg)
1190
1191     @staticmethod
1192     def vpp_ipsec_add_multiple_tunnels(
1193             nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1194             tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1195         """Create multiple IPsec tunnels between two VPP nodes.
1196
1197         :param nodes: VPP nodes to create tunnels.
1198         :param interface1: Interface name or sw_if_index on node 1.
1199         :param interface2: Interface name or sw_if_index on node 2.
1200         :param n_tunnels: Number of tunnels to create.
1201         :param crypto_alg: The encryption algorithm name.
1202         :param integ_alg: The integrity algorithm name.
1203         :param tunnel_ip1: Tunnel node1 IPv4 address.
1204         :param tunnel_ip2: Tunnel node2 IPv4 address.
1205         :param raddr_ip1: Policy selector remote IPv4 start address for the
1206             first tunnel in direction node1->node2.
1207         :param raddr_ip2: Policy selector remote IPv4 start address for the
1208             first tunnel in direction node2->node1.
1209         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1210             addresses. Valid values are from 1 to 32.
1211         :type nodes: dict
1212         :type interface1: str or int
1213         :type interface2: str or int
1214         :type n_tunnels: int
1215         :type crypto_alg: CryptoAlg
1216         :type integ_alg: IntegAlg
1217         :type tunnel_ip1: str
1218         :type tunnel_ip2: str
1219         :type raddr_ip1: string
1220         :type raddr_ip2: string
1221         :type raddr_range: int
1222         """
1223         spd_id = 1
1224         p_hi = 100
1225         p_lo = 10
1226         sa_id_1 = 100000
1227         sa_id_2 = 200000
1228         spi_1 = 300000
1229         spi_2 = 400000
1230
1231         crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1232         integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \
1233             if integ_alg else ''
1234
1235         IPsecUtil.vpp_ipsec_set_ip_route(
1236             nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1237             interface1, raddr_range)
1238         IPsecUtil.vpp_ipsec_set_ip_route(
1239             nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1240             interface2, raddr_range)
1241
1242         IPsecUtil.vpp_ipsec_add_spd(
1243             nodes['DUT1'], spd_id)
1244         IPsecUtil.vpp_ipsec_spd_add_if(
1245             nodes['DUT1'], spd_id, interface1)
1246         IPsecUtil.vpp_ipsec_policy_add(
1247             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1248             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1249         IPsecUtil.vpp_ipsec_policy_add(
1250             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1251             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1252
1253         IPsecUtil.vpp_ipsec_add_spd(
1254             nodes['DUT2'], spd_id)
1255         IPsecUtil.vpp_ipsec_spd_add_if(
1256             nodes['DUT2'], spd_id, interface2)
1257         IPsecUtil.vpp_ipsec_policy_add(
1258             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1259             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1260         IPsecUtil.vpp_ipsec_policy_add(
1261             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1262             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1263
1264         IPsecUtil.vpp_ipsec_add_sad_entries(
1265             nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1266             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1267
1268         IPsecUtil.vpp_ipsec_spd_add_entries(
1269             nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
1270
1271         IPsecUtil.vpp_ipsec_add_sad_entries(
1272             nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1273             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1274
1275         IPsecUtil.vpp_ipsec_spd_add_entries(
1276             nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
1277
1278         IPsecUtil.vpp_ipsec_add_sad_entries(
1279             nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1280             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1281
1282         IPsecUtil.vpp_ipsec_spd_add_entries(
1283             nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
1284
1285         IPsecUtil.vpp_ipsec_add_sad_entries(
1286             nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1287             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1288
1289         IPsecUtil.vpp_ipsec_spd_add_entries(
1290             nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
1291
1292     @staticmethod
1293     def vpp_ipsec_show(node):
1294         """Run "show ipsec" debug CLI command.
1295
1296         :param node: Node to run command on.
1297         :type node: dict
1298         """
1299         PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')