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