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