PAPI: Fix PyLint errors
[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 from random import choice
18 from string import letters
19 from ipaddress import ip_network, ip_address
20
21 from enum import Enum, IntEnum
22
23 from resources.libraries.python.PapiExecutor import PapiExecutor
24 from resources.libraries.python.topology import Topology
25 from resources.libraries.python.VatExecutor import VatExecutor
26 from resources.libraries.python.VatJsonUtil import VatJsonUtil
27
28
29 def gen_key(length):
30     """Generate random string as a key.
31
32     :param length: Length of generated payload.
33     :type length: int
34     :returns: The generated payload.
35     :rtype: str
36     """
37     return ''.join(choice(letters) for _ in range(length)).encode('hex')
38
39 class PolicyAction(Enum):
40     """Policy actions."""
41     BYPASS = 'bypass'
42     DISCARD = 'discard'
43     PROTECT = 'protect'
44
45     def __init__(self, string):
46         self.string = string
47
48
49 class CryptoAlg(Enum):
50     """Encryption algorithms."""
51     AES_CBC_128 = ('aes-cbc-128', 'AES-CBC', 16)
52     AES_CBC_192 = ('aes-cbc-192', 'AES-CBC', 24)
53     AES_CBC_256 = ('aes-cbc-256', 'AES-CBC', 32)
54     AES_GCM_128 = ('aes-gcm-128', 'AES-GCM', 20)
55     AES_GCM_256 = ('aes-gcm-256', 'AES-GCM', 40)
56
57     def __init__(self, alg_name, scapy_name, key_len):
58         self.alg_name = alg_name
59         self.scapy_name = scapy_name
60         self.key_len = key_len
61
62
63 class IntegAlg(Enum):
64     """Integrity algorithm."""
65     SHA1_96 = ('sha1-96', 'HMAC-SHA1-96', 20)
66     SHA_256_128 = ('sha-256-128', 'SHA2-256-128', 32)
67     SHA_384_192 = ('sha-384-192', 'SHA2-384-192', 48)
68     SHA_512_256 = ('sha-512-256', 'SHA2-512-256', 64)
69     AES_GCM_128 = ('aes-gcm-128', 'AES-GCM', 20)
70     AES_GCM_256 = ('aes-gcm-256', 'AES-GCM', 40)
71
72     def __init__(self, alg_name, scapy_name, key_len):
73         self.alg_name = alg_name
74         self.scapy_name = scapy_name
75         self.key_len = key_len
76
77
78 class IPsecProto(IntEnum):
79     """IPsec protocol."""
80     ESP = 1
81     SEC_AH = 0
82
83
84 class IPsecUtil(object):
85     """IPsec utilities."""
86
87     @staticmethod
88     def policy_action_bypass():
89         """Return policy action bypass.
90
91         :returns: PolicyAction enum BYPASS object.
92         :rtype: PolicyAction
93         """
94         return PolicyAction.BYPASS
95
96     @staticmethod
97     def policy_action_discard():
98         """Return policy action discard.
99
100         :returns: PolicyAction enum DISCARD object.
101         :rtype: PolicyAction
102         """
103         return PolicyAction.DISCARD
104
105     @staticmethod
106     def policy_action_protect():
107         """Return policy action protect.
108
109         :returns: PolicyAction enum PROTECT object.
110         :rtype: PolicyAction
111         """
112         return PolicyAction.PROTECT
113
114     @staticmethod
115     def crypto_alg_aes_cbc_128():
116         """Return encryption algorithm aes-cbc-128.
117
118         :returns: CryptoAlg enum AES_CBC_128 object.
119         :rtype: CryptoAlg
120         """
121         return CryptoAlg.AES_CBC_128
122
123     @staticmethod
124     def crypto_alg_aes_cbc_192():
125         """Return encryption algorithm aes-cbc-192.
126
127         :returns: CryptoAlg enum AES_CBC_192 objec.
128         :rtype: CryptoAlg
129         """
130         return CryptoAlg.AES_CBC_192
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_sha1_96():
183         """Return integrity algorithm SHA1-96.
184
185         :returns: IntegAlg enum SHA1_96 object.
186         :rtype: IntegAlg
187         """
188         return IntegAlg.SHA1_96
189
190     @staticmethod
191     def integ_alg_sha_256_128():
192         """Return integrity algorithm SHA-256-128.
193
194         :returns: IntegAlg enum SHA_256_128 object.
195         :rtype: IntegAlg
196         """
197         return IntegAlg.SHA_256_128
198
199     @staticmethod
200     def integ_alg_sha_384_192():
201         """Return integrity algorithm SHA-384-192.
202
203         :returns: IntegAlg enum SHA_384_192 object.
204         :rtype: IntegAlg
205         """
206         return IntegAlg.SHA_384_192
207
208     @staticmethod
209     def integ_alg_sha_512_256():
210         """Return integrity algorithm SHA-512-256.
211
212         :returns: IntegAlg enum SHA_512_256 object.
213         :rtype: IntegAlg
214         """
215         return IntegAlg.SHA_512_256
216
217     @staticmethod
218     def integ_alg_aes_gcm_128():
219         """Return integrity algorithm AES-GCM-128.
220
221         :returns: IntegAlg enum AES_GCM_128 object.
222         :rtype: IntegAlg
223         """
224         return IntegAlg.AES_GCM_128
225
226     @staticmethod
227     def integ_alg_aes_gcm_256():
228         """Return integrity algorithm AES-GCM-256.
229
230         :returns: IntegAlg enum AES_GCM_256 object.
231         :rtype: IntegAlg
232         """
233         return IntegAlg.AES_GCM_256
234
235     @staticmethod
236     def get_integ_alg_key_len(integ_alg):
237         """Return integrity algorithm key length.
238
239         :param integ_alg: Integrity algorithm.
240         :type integ_alg: IntegAlg
241         :returns: Key length.
242         :rtype: int
243         """
244         return integ_alg.key_len
245
246     @staticmethod
247     def get_integ_alg_scapy_name(integ_alg):
248         """Return integrity algorithm scapy name.
249
250         :param integ_alg: Integrity algorithm.
251         :type integ_alg: IntegAlg
252         :returns: Algorithm scapy name.
253         :rtype: str
254         """
255         return integ_alg.scapy_name
256
257     @staticmethod
258     def ipsec_proto_esp():
259         """Return IPSec protocol ESP.
260
261         :returns: IPsecProto enum ESP object.
262         :rtype: IPsecProto
263         """
264         return int(IPsecProto.ESP)
265
266     @staticmethod
267     def ipsec_proto_ah():
268         """Return IPSec protocol AH.
269
270         :returns: IPsecProto enum AH object.
271         :rtype: IPsecProto
272         """
273         return int(IPsecProto.SEC_AH)
274
275     @staticmethod
276     def vpp_ipsec_select_backend(node, protocol, index=1):
277         """Select IPsec backend.
278
279         :param node: VPP node to select IPsec backend on.
280         :param protocol: IPsec protocol.
281         :param index: Backend index.
282         :type node: dict
283         :type protocol: IPsecProto
284         :type index: int
285         :raises RuntimeError: If failed to select IPsec backend or if no API
286             reply received.
287         """
288
289         cmd = 'ipsec_select_backend'
290         cmd_reply = 'ipsec_select_backend_reply'
291         err_msg = 'Failed to select IPsec backend on host {host}'.format(
292             host=node['host'])
293         args = dict(protocol=protocol, index=index)
294         with PapiExecutor(node) as papi_exec:
295             papi_resp = papi_exec.add(cmd, **args).execute_should_pass(err_msg)
296         data = papi_resp.reply[0]['api_reply'][cmd_reply]
297         if data['retval'] != 0:
298             raise RuntimeError('Failed to select IPsec backend on host {host}'.
299                                format(host=node['host']))
300
301     @staticmethod
302     def vpp_ipsec_backend_dump(node):
303         """Dump IPsec backends.
304
305         :param node: VPP node to dump IPsec backend on.
306         :type node: dict
307         """
308
309         err_msg = 'Failed to dump IPsec backends on host {host}'.format(
310             host=node['host'])
311         with PapiExecutor(node) as papi_exec:
312             papi_exec.add('ipsec_backend_dump').execute_should_pass(
313                 err_msg, process_reply=False)
314
315     @staticmethod
316     def vpp_ipsec_add_sad_entry(node, sad_id, spi, crypto_alg, crypto_key,
317                                 integ_alg, integ_key, tunnel_src=None,
318                                 tunnel_dst=None):
319         """Create Security Association Database entry on the VPP node.
320
321         :param node: VPP node to add SAD entry on.
322         :param sad_id: SAD entry ID.
323         :param spi: Security Parameter Index of this SAD entry.
324         :param crypto_alg: The encryption algorithm name.
325         :param crypto_key: The encryption key string.
326         :param integ_alg: The integrity algorithm name.
327         :param integ_key: The integrity key string.
328         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
329             specified ESP transport mode is used.
330         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
331             not specified ESP transport mode is used.
332         :type node: dict
333         :type sad_id: int
334         :type spi: int
335         :type crypto_alg: CryptoAlg
336         :type crypto_key: str
337         :type integ_alg: IntegAlg
338         :type integ_key: str
339         :type tunnel_src: str
340         :type tunnel_dst: str
341         """
342         ckey = crypto_key.encode('hex')
343         ikey = integ_key.encode('hex')
344         tunnel = 'tunnel-src {0} tunnel-dst {1}'.format(tunnel_src, tunnel_dst)\
345             if tunnel_src is not None and tunnel_dst is not None else ''
346
347         out = VatExecutor.cmd_from_template(node,
348                                             'ipsec/ipsec_sad_add_entry.vat',
349                                             sad_id=sad_id, spi=spi,
350                                             calg=crypto_alg.alg_name, ckey=ckey,
351                                             ialg=integ_alg.alg_name, ikey=ikey,
352                                             tunnel=tunnel)
353         VatJsonUtil.verify_vat_retval(
354             out[0],
355             err_msg='Add SAD entry failed on {0}'.format(node['host']))
356
357     @staticmethod
358     def vpp_ipsec_add_sad_entries(node, n_entries, sad_id, spi, crypto_alg,
359                                   crypto_key, integ_alg, integ_key,
360                                   tunnel_src=None, tunnel_dst=None):
361         """Create multiple Security Association Database entries on VPP node.
362
363         :param node: VPP node to add SAD entry on.
364         :param n_entries: Number of SAD entries to be created.
365         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
366             id incremented by 1.
367         :param spi: Security Parameter Index of first SAD entry. All subsequent
368             SAD entries will have spi incremented by 1.
369         :param crypto_alg: The encryption algorithm name.
370         :param crypto_key: The encryption key string.
371         :param integ_alg: The integrity algorithm name.
372         :param integ_key: The integrity key string.
373         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
374             specified ESP transport mode is used.
375         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
376             not specified ESP transport mode is used.
377         :type node: dict
378         :type n_entries: int
379         :type sad_id: int
380         :type spi: int
381         :type crypto_alg: CryptoAlg
382         :type crypto_key: str
383         :type integ_alg: IntegAlg
384         :type integ_key: str
385         :type tunnel_src: str
386         :type tunnel_dst: str
387         """
388         tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.format(sad_id)
389
390         addr_incr = 1 << (32 - 24)
391
392         with open(tmp_filename, 'w') as tmp_file:
393             for i in range(0, n_entries):
394                 integ = ''
395                 if not crypto_alg.alg_name.startswith('aes-gcm-'):
396                     integ = (
397                         'integ-alg {integ_alg} integ-key {integ_key}'.
398                         format(
399                             integ_alg=integ_alg.alg_name,
400                             integ_key=integ_key))
401                 tunnel = (
402                     'tunnel-src {laddr} tunnel-dst {raddr}'.
403                     format(
404                         laddr=ip_address(unicode(tunnel_src)) + i * addr_incr,
405                         raddr=ip_address(unicode(tunnel_dst)) + i * addr_incr)
406                     if tunnel_src and tunnel_dst is not None else '')
407                 conf = (
408                     'exec ipsec sa add {sad_id} esp spi {spi} '
409                     'crypto-alg {crypto_alg} crypto-key {crypto_key} '
410                     '{integ} {tunnel}\n'.
411                     format(
412                         sad_id=sad_id + i,
413                         spi=spi + i,
414                         crypto_alg=crypto_alg.alg_name,
415                         crypto_key=crypto_key,
416                         integ=integ,
417                         tunnel=tunnel))
418                 tmp_file.write(conf)
419         vat = VatExecutor()
420         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
421                            copy_on_execute=True)
422         os.remove(tmp_filename)
423
424     @staticmethod
425     def vpp_ipsec_set_ip_route(node, n_tunnels, tunnel_src, traffic_addr,
426                                tunnel_dst, interface, raddr_range):
427         """Set IP address and route on interface.
428
429         :param node: VPP node to add config on.
430         :param n_tunnels: Number of tunnels to create.
431         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
432         :param traffic_addr: Traffic destination IP address to route.
433         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
434         :param interface: Interface key on node 1.
435         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
436             addresses. Valid values are from 1 to 32.
437         :type node: dict
438         :type n_tunnels: int
439         :type tunnel_src: str
440         :type traffic_addr: str
441         :type tunnel_dst: str
442         :type interface: str
443         :type raddr_range: int
444         """
445         tmp_filename = '/tmp/ipsec_set_ip.script'
446
447         addr_incr = 1 << (32 - raddr_range)
448
449         with open(tmp_filename, 'w') as tmp_file:
450             for i in range(0, n_tunnels):
451                 conf = (
452                     'exec set interface ip address {interface} {laddr}/24\n'
453                     'exec ip route add {taddr}/32 via {raddr} {interface}\n'.
454                     format(
455                         interface=Topology.get_interface_name(node, interface),
456                         laddr=ip_address(unicode(tunnel_src)) + i * addr_incr,
457                         raddr=ip_address(unicode(tunnel_dst)) + i * addr_incr,
458                         taddr=ip_address(unicode(traffic_addr)) + i))
459                 tmp_file.write(conf)
460         vat = VatExecutor()
461         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
462                            copy_on_execute=True)
463         os.remove(tmp_filename)
464
465     @staticmethod
466     def vpp_ipsec_sa_set_key(node, sa_id, crypto_key, integ_key):
467         """Update Security Association (SA) keys.
468
469         :param node: VPP node to update SA keys.
470         :param sa_id: SAD entry ID.
471         :param crypto_key: The encryption key string.
472         :param integ_key: The integrity key string.
473         :type node: dict
474         :type sa_id: int
475         :type crypto_key: str
476         :type integ_key: str
477         """
478         ckey = crypto_key.encode('hex')
479         ikey = integ_key.encode('hex')
480
481         out = VatExecutor.cmd_from_template(
482             node, 'ipsec/ipsec_sa_set_key.vat', json_param=False, sa_id=sa_id,
483             ckey=ckey, ikey=ikey)
484         VatJsonUtil.verify_vat_retval(
485             out[0],
486             err_msg='Update SA key failed on {0}'.format(node['host']))
487
488     @staticmethod
489     def vpp_ipsec_add_spd(node, spd_id):
490         """Create Security Policy Database on the VPP node.
491
492         :param node: VPP node to add SPD on.
493         :param spd_id: SPD ID.
494         :type node: dict
495         :type spd_id: int
496         """
497         out = VatExecutor.cmd_from_template(node, 'ipsec/ipsec_spd_add.vat',
498                                             spd_id=spd_id)
499         VatJsonUtil.verify_vat_retval(
500             out[0],
501             err_msg='Add SPD {0} failed on {1}'.format(spd_id, node['host']))
502
503     @staticmethod
504     def vpp_ipsec_spd_add_if(node, spd_id, interface):
505         """Add interface to the Security Policy Database.
506
507         :param node: VPP node.
508         :param spd_id: SPD ID to add interface on.
509         :param interface: Interface name or sw_if_index.
510         :type node: dict
511         :type spd_id: int
512         :type interface: str or int
513         """
514         sw_if_index = Topology.get_interface_sw_index(node, interface)\
515             if isinstance(interface, basestring) else interface
516
517         out = VatExecutor.cmd_from_template(node,
518                                             'ipsec/ipsec_interface_add_spd.vat',
519                                             spd_id=spd_id, sw_if_id=sw_if_index)
520         VatJsonUtil.verify_vat_retval(
521             out[0],
522             err_msg='Add interface {0} to SPD {1} failed on {2}'.format(
523                 interface, spd_id, node['host']))
524
525     @staticmethod
526     def vpp_ipsec_policy_add(node, spd_id, priority, action, inbound=True,
527                              sa_id=None, laddr_range=None, raddr_range=None,
528                              proto=None, lport_range=None, rport_range=None,
529                              is_ipv6=False):
530         """Create Security Policy Database entry on the VPP node.
531
532         :param node: VPP node to add SPD entry on.
533         :param spd_id: SPD ID to add entry on.
534         :param priority: SPD entry priority, higher number = higher priority.
535         :param action: Policy action.
536         :param inbound: If True policy is for inbound traffic, otherwise
537             outbound.
538         :param sa_id: SAD entry ID for protect action.
539         :param laddr_range: Policy selector local IPv4 or IPv6 address range in
540             format IP/prefix or IP/mask. If no mask is provided,
541             it's considered to be /32.
542         :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
543             format IP/prefix or IP/mask. If no mask is provided,
544             it's considered to be /32.
545         :param proto: Policy selector next layer protocol number.
546         :param lport_range: Policy selector local TCP/UDP port range in format
547             <port_start>-<port_end>.
548         :param rport_range: Policy selector remote TCP/UDP port range in format
549             <port_start>-<port_end>.
550         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
551             not defined so it will default to address ::/0, otherwise False.
552         :type node: dict
553         :type spd_id: int
554         :type priority: int
555         :type action: PolicyAction
556         :type inbound: bool
557         :type sa_id: int
558         :type laddr_range: string
559         :type raddr_range: string
560         :type proto: int
561         :type lport_range: string
562         :type rport_range: string
563         :type is_ipv6: bool
564         """
565         direction = 'inbound' if inbound else 'outbound'
566
567         if laddr_range is None and is_ipv6:
568             laddr_range = '::/0'
569
570         if raddr_range is None and is_ipv6:
571             raddr_range = '::/0'
572
573         act_str = action.value
574         if PolicyAction.PROTECT == action and sa_id is not None:
575             act_str += ' sa {0}'.format(sa_id)
576
577         selector = ''
578         if laddr_range is not None:
579             net = ip_network(unicode(laddr_range), strict=False)
580             selector += 'local-ip-range {0} - {1} '.format(
581                 net.network_address, net.broadcast_address)
582         if raddr_range is not None:
583             net = ip_network(unicode(raddr_range), strict=False)
584             selector += 'remote-ip-range {0} - {1} '.format(
585                 net.network_address, net.broadcast_address)
586         if proto is not None:
587             selector += 'protocol {0} '.format(proto)
588         if lport_range is not None:
589             selector += 'local-port-range {0} '.format(lport_range)
590         if rport_range is not None:
591             selector += 'remote-port-range {0} '.format(rport_range)
592
593         out = VatExecutor.cmd_from_template(
594             node, 'ipsec/ipsec_policy_add.vat', json_param=False, spd_id=spd_id,
595             priority=priority, action=act_str, direction=direction,
596             selector=selector)
597         VatJsonUtil.verify_vat_retval(
598             out[0],
599             err_msg='Add IPsec policy ID {0} failed on {1}'.format(
600                 spd_id, node['host']))
601
602     @staticmethod
603     def vpp_ipsec_spd_add_entries(node, n_entries, spd_id, priority, inbound,
604                                   sa_id, raddr_ip):
605         """Create multiple Security Policy Database entries on the VPP node.
606
607         :param node: VPP node to add SPD entries on.
608         :param n_entries: Number of SPD entries to be added.
609         :param spd_id: SPD ID to add entries on.
610         :param priority: SPD entries priority, higher number = higher priority.
611         :param inbound: If True policy is for inbound traffic, otherwise
612             outbound.
613         :param sa_id: SAD entry ID for first entry. Each subsequent entry will
614             SAD entry ID incremented by 1.
615         :param raddr_ip: Policy selector remote IPv4 start address for the first
616             entry. Remote IPv4 end address will be calculated depending on
617             raddr_range parameter. Each subsequent entry will have start address
618             next after IPv4 end address of previous entry.
619         :type node: dict
620         :type n_entries: int
621         :type spd_id: int
622         :type priority: int
623         :type inbound: bool
624         :type sa_id: int
625         :type raddr_ip: string
626         """
627         tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.format(sa_id)
628
629         with open(tmp_filename, 'w') as tmp_file:
630             for i in range(0, n_entries):
631                 raddr_s = ip_address(unicode(raddr_ip)) + i
632                 raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1
633                 tunnel = (
634                     'exec ipsec policy add spd {spd_id} priority {priority} '
635                     '{direction} action protect sa {sa_id} '
636                     'remote-ip-range {raddr_s} - {raddr_e}\n'.
637                     format(
638                         spd_id=spd_id,
639                         priority=priority,
640                         direction='inbound' if inbound else 'outbound',
641                         sa_id=sa_id+i,
642                         raddr_s=raddr_s,
643                         raddr_e=raddr_e))
644                 tmp_file.write(tunnel)
645         vat = VatExecutor()
646         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
647                            copy_on_execute=True)
648         os.remove(tmp_filename)
649
650     @staticmethod
651     def vpp_ipsec_create_tunnel_interfaces(nodes, if1_ip_addr, if2_ip_addr,
652                                            if1_key, if2_key, n_tunnels,
653                                            crypto_alg, integ_alg, raddr_ip1,
654                                            raddr_ip2, raddr_range):
655         """Create multiple IPsec tunnel interfaces between two VPP nodes.
656
657         :param nodes: VPP nodes to create tunnel interfaces.
658         :param if1_ip_addr: VPP node 1 interface IP4 address.
659         :param if2_ip_addr: VPP node 2 interface IP4 address.
660         :param if1_key: VPP node 1 interface key from topology file.
661         :param if2_key: VPP node 2 interface key from topology file.
662         :param n_tunnels: Number of tunnell interfaces to create.
663         :param crypto_alg: The encryption algorithm name.
664         :param integ_alg: The integrity algorithm name.
665         :param raddr_ip1: Policy selector remote IPv4 start address for the
666             first tunnel in direction node1->node2.
667         :param raddr_ip2: Policy selector remote IPv4 start address for the
668             first tunnel in direction node2->node1.
669         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
670             addresses. Valid values are from 1 to 32.
671         :type nodes: dict
672         :type if1_ip_addr: str
673         :type if2_ip_addr: str
674         :type if1_key: str
675         :type if2_key: str
676         :type n_tunnels: int
677         :type crypto_alg: CryptoAlg
678         :type integ_alg: IntegAlg
679         :type raddr_ip1: string
680         :type raddr_ip2: string
681         :type raddr_range: int
682         """
683         spi_1 = 100000
684         spi_2 = 200000
685         addr_incr = 1 << (32 - raddr_range)
686
687         tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
688         tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
689
690         vat = VatExecutor()
691
692         with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
693             for i in range(0, n_tunnels):
694                 ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
695                 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
696                 integ = ''
697                 if not crypto_alg.alg_name.startswith('aes-gcm-'):
698                     integ = (
699                         'integ_alg {integ_alg} '
700                         'local_integ_key {local_integ_key} '
701                         'remote_integ_key {remote_integ_key} '
702                         .format(
703                             integ_alg=integ_alg.alg_name,
704                             local_integ_key=ikey,
705                             remote_integ_key=ikey))
706                 tmp_f1.write(
707                     'exec set interface ip address {uifc} {laddr}/24\n'
708                     'ipsec_tunnel_if_add_del '
709                     'local_spi {local_spi} '
710                     'remote_spi {remote_spi} '
711                     'crypto_alg {crypto_alg} '
712                     'local_crypto_key {local_crypto_key} '
713                     'remote_crypto_key {remote_crypto_key} '
714                     '{integ} '
715                     'local_ip {laddr} '
716                     'remote_ip {raddr}\n'
717                     .format(
718                         local_spi=spi_1 + i,
719                         remote_spi=spi_2 + i,
720                         crypto_alg=crypto_alg.alg_name,
721                         local_crypto_key=ckey,
722                         remote_crypto_key=ckey,
723                         integ=integ,
724                         laddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr,
725                         raddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr,
726                         uifc=Topology.get_interface_name(nodes['DUT1'],
727                                                          if1_key)))
728                 tmp_f2.write(
729                     'exec set interface ip address {uifc} {laddr}/24\n'
730                     'ipsec_tunnel_if_add_del '
731                     'local_spi {local_spi} '
732                     'remote_spi {remote_spi} '
733                     'crypto_alg {crypto_alg} '
734                     'local_crypto_key {local_crypto_key} '
735                     'remote_crypto_key {remote_crypto_key} '
736                     '{integ} '
737                     'local_ip {laddr} '
738                     'remote_ip {raddr}\n'
739                     .format(
740                         local_spi=spi_2 + i,
741                         remote_spi=spi_1 + i,
742                         crypto_alg=crypto_alg.alg_name,
743                         local_crypto_key=ckey,
744                         remote_crypto_key=ckey,
745                         integ=integ,
746                         laddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr,
747                         raddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr,
748                         uifc=Topology.get_interface_name(nodes['DUT2'],
749                                                          if2_key)))
750         vat.execute_script(tmp_fn1, nodes['DUT1'], timeout=300, json_out=False,
751                            copy_on_execute=True)
752         vat.execute_script(tmp_fn2, nodes['DUT2'], timeout=300, json_out=False,
753                            copy_on_execute=True)
754         os.remove(tmp_fn1)
755         os.remove(tmp_fn2)
756
757         with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
758             for i in range(0, n_tunnels):
759                 tmp_f1.write(
760                     'exec set interface unnumbered ipsec{i} use {uifc}\n'
761                     'exec set interface state ipsec{i} up\n'
762                     'exec ip route add {taddr}/32 via {raddr} ipsec{i}\n'
763                     .format(
764                         taddr=ip_address(unicode(raddr_ip2)) + i,
765                         raddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr,
766                         i=i,
767                         uifc=Topology.get_interface_name(nodes['DUT1'],
768                                                          if1_key)))
769                 tmp_f2.write(
770                     'exec set interface unnumbered ipsec{i} use {uifc}\n'
771                     'exec set interface state ipsec{i} up\n'
772                     'exec ip route add {taddr}/32 via {raddr} ipsec{i}\n'
773                     .format(
774                         taddr=ip_address(unicode(raddr_ip1)) + i,
775                         raddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr,
776                         i=i,
777                         uifc=Topology.get_interface_name(nodes['DUT2'],
778                                                          if2_key)))
779         vat.execute_script(tmp_fn1, nodes['DUT1'], timeout=300, json_out=False,
780                            copy_on_execute=True)
781         vat.execute_script(tmp_fn2, nodes['DUT2'], timeout=300, json_out=False,
782                            copy_on_execute=True)
783         os.remove(tmp_fn1)
784         os.remove(tmp_fn2)
785
786     @staticmethod
787     def vpp_ipsec_add_multiple_tunnels(nodes, interface1, interface2,
788                                        n_tunnels, crypto_alg, integ_alg,
789                                        tunnel_ip1, tunnel_ip2, raddr_ip1,
790                                        raddr_ip2, raddr_range):
791         """Create multiple IPsec tunnels between two VPP nodes.
792
793         :param nodes: VPP nodes to create tunnels.
794         :param interface1: Interface name or sw_if_index on node 1.
795         :param interface2: Interface name or sw_if_index on node 2.
796         :param n_tunnels: Number of tunnels to create.
797         :param crypto_alg: The encryption algorithm name.
798         :param integ_alg: The integrity algorithm name.
799         :param tunnel_ip1: Tunnel node1 IPv4 address.
800         :param tunnel_ip2: Tunnel node2 IPv4 address.
801         :param raddr_ip1: Policy selector remote IPv4 start address for the
802             first tunnel in direction node1->node2.
803         :param raddr_ip2: Policy selector remote IPv4 start address for the
804             first tunnel in direction node2->node1.
805         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
806             addresses. Valid values are from 1 to 32.
807         :type nodes: dict
808         :type interface1: str or int
809         :type interface2: str or int
810         :type n_tunnels: int
811         :type crypto_alg: CryptoAlg
812         :type integ_alg: str
813         :type tunnel_ip1: str
814         :type tunnel_ip2: str
815         :type raddr_ip1: string
816         :type raddr_ip2: string
817         :type raddr_range: int
818         """
819         spd_id = 1
820         p_hi = 100
821         p_lo = 10
822         sa_id_1 = 100000
823         sa_id_2 = 200000
824         spi_1 = 300000
825         spi_2 = 400000
826
827         crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
828         integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
829
830         IPsecUtil.vpp_ipsec_set_ip_route(
831             nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
832             interface1, raddr_range)
833         IPsecUtil.vpp_ipsec_set_ip_route(
834             nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
835             interface2, raddr_range)
836
837         IPsecUtil.vpp_ipsec_add_spd(
838             nodes['DUT1'], spd_id)
839         IPsecUtil.vpp_ipsec_spd_add_if(
840             nodes['DUT1'], spd_id, interface1)
841         IPsecUtil.vpp_ipsec_policy_add(
842             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
843             proto=50)
844         IPsecUtil.vpp_ipsec_policy_add(
845             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
846             proto=50)
847
848         IPsecUtil.vpp_ipsec_add_spd(
849             nodes['DUT2'], spd_id)
850         IPsecUtil.vpp_ipsec_spd_add_if(
851             nodes['DUT2'], spd_id, interface2)
852         IPsecUtil.vpp_ipsec_policy_add(
853             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
854             proto=50)
855         IPsecUtil.vpp_ipsec_policy_add(
856             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
857             proto=50)
858
859         IPsecUtil.vpp_ipsec_add_sad_entries(
860             nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
861             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
862
863         IPsecUtil.vpp_ipsec_spd_add_entries(
864             nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
865
866         IPsecUtil.vpp_ipsec_add_sad_entries(
867             nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
868             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
869
870         IPsecUtil.vpp_ipsec_spd_add_entries(
871             nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
872
873         IPsecUtil.vpp_ipsec_add_sad_entries(
874             nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
875             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
876
877         IPsecUtil.vpp_ipsec_spd_add_entries(
878             nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
879
880         IPsecUtil.vpp_ipsec_add_sad_entries(
881             nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
882             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
883
884         IPsecUtil.vpp_ipsec_spd_add_entries(
885             nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
886
887     @staticmethod
888     def vpp_ipsec_show(node):
889         """Run "show ipsec" debug CLI command.
890
891         :param node: Node to run command on.
892         :type node: dict
893         """
894         VatExecutor().execute_script('ipsec/ipsec_show.vat', node,
895                                      json_out=False)