docs/lab: merged testbed specifications, separated out detailed HW and BIOS configs.
[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
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 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     SEC_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.SEC_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             # After API change there is returned VPP internal enum object
309             # representing VPP IPSEC protocol instead of integer representation
310             # so JSON fails to decode it - we need to check if it is Python API
311             # bug or we need to adapt vpp_papi_provider to correctly encode
312             # such object into JSON
313             # api_reply = papi_executor.get_papi_reply()
314             api_reply = papi_executor.get_papi_stdout()
315
316         if api_reply is not None:
317             logger.trace('IPsec backend dump\n{dump}'.format(dump=api_reply))
318         else:
319             raise PapiError('No reply received for ipsec_select_backend API '
320                             'command on host {host}'.format(host=node['host']))
321
322     @staticmethod
323     def vpp_ipsec_add_sad_entry(node, sad_id, spi, crypto_alg, crypto_key,
324                                 integ_alg, integ_key, tunnel_src=None,
325                                 tunnel_dst=None):
326         """Create Security Association Database entry on the VPP node.
327
328         :param node: VPP node to add SAD entry on.
329         :param sad_id: SAD entry ID.
330         :param spi: Security Parameter Index of this SAD entry.
331         :param crypto_alg: The encryption algorithm name.
332         :param crypto_key: The encryption key string.
333         :param integ_alg: The integrity algorithm name.
334         :param integ_key: The integrity key string.
335         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
336             specified ESP transport mode is used.
337         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
338             not specified ESP transport mode is used.
339         :type node: dict
340         :type sad_id: int
341         :type spi: int
342         :type crypto_alg: CryptoAlg
343         :type crypto_key: str
344         :type integ_alg: IntegAlg
345         :type integ_key: str
346         :type tunnel_src: str
347         :type tunnel_dst: str
348         """
349         ckey = crypto_key.encode('hex')
350         ikey = integ_key.encode('hex')
351         tunnel = 'tunnel-src {0} tunnel-dst {1}'.format(tunnel_src, tunnel_dst)\
352             if tunnel_src is not None and tunnel_dst is not None else ''
353
354         out = VatExecutor.cmd_from_template(node,
355                                             'ipsec/ipsec_sad_add_entry.vat',
356                                             sad_id=sad_id, spi=spi,
357                                             calg=crypto_alg.alg_name, ckey=ckey,
358                                             ialg=integ_alg.alg_name, ikey=ikey,
359                                             tunnel=tunnel)
360         VatJsonUtil.verify_vat_retval(
361             out[0],
362             err_msg='Add SAD entry failed on {0}'.format(node['host']))
363
364     @staticmethod
365     def vpp_ipsec_add_sad_entries(node, n_entries, sad_id, spi, crypto_alg,
366                                   crypto_key, integ_alg, integ_key,
367                                   tunnel_src=None, tunnel_dst=None):
368         """Create multiple Security Association Database entries on VPP node.
369
370         :param node: VPP node to add SAD entry on.
371         :param n_entries: Number of SAD entries to be created.
372         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
373             id incremented by 1.
374         :param spi: Security Parameter Index of first SAD entry. All subsequent
375             SAD entries will have spi incremented by 1.
376         :param crypto_alg: The encryption algorithm name.
377         :param crypto_key: The encryption key string.
378         :param integ_alg: The integrity algorithm name.
379         :param integ_key: The integrity key string.
380         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
381             specified ESP transport mode is used.
382         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
383             not specified ESP transport mode is used.
384         :type node: dict
385         :type n_entries: int
386         :type sad_id: int
387         :type spi: int
388         :type crypto_alg: CryptoAlg
389         :type crypto_key: str
390         :type integ_alg: IntegAlg
391         :type integ_key: str
392         :type tunnel_src: str
393         :type tunnel_dst: str
394         """
395         tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.format(sad_id)
396         ckey = crypto_key.encode('hex')
397         ikey = integ_key.encode('hex')
398         tunnel = 'tunnel-src {0} tunnel-dst {1}'.format(tunnel_src, tunnel_dst)\
399             if tunnel_src is not None and tunnel_dst is not None else ''
400
401         integ = 'integ-alg {0} integ-key {1}'.format(integ_alg.alg_name, ikey)\
402             if crypto_alg.alg_name != 'aes-gcm-128' else ''
403
404         with open(tmp_filename, 'w') as tmp_file:
405             for i in range(0, n_entries):
406                 buf_str = 'exec ipsec sa add {0} esp spi {1} ' \
407                           'crypto-alg {2} crypto-key {3} {4} {5}\n'.format(
408                               sad_id+i, spi+i, crypto_alg.alg_name, ckey, integ,
409                               tunnel)
410                 tmp_file.write(buf_str)
411         vat = VatExecutor()
412         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
413                            copy_on_execute=True)
414         os.remove(tmp_filename)
415
416     @staticmethod
417     def vpp_ipsec_sa_set_key(node, sa_id, crypto_key, integ_key):
418         """Update Security Association (SA) keys.
419
420         :param node: VPP node to update SA keys.
421         :param sa_id: SAD entry ID.
422         :param crypto_key: The encryption key string.
423         :param integ_key: The integrity key string.
424         :type node: dict
425         :type sa_id: int
426         :type crypto_key: str
427         :type integ_key: str
428         """
429         ckey = crypto_key.encode('hex')
430         ikey = integ_key.encode('hex')
431
432         out = VatExecutor.cmd_from_template(
433             node, 'ipsec/ipsec_sa_set_key.vat', json_param=False, sa_id=sa_id,
434             ckey=ckey, ikey=ikey)
435         VatJsonUtil.verify_vat_retval(
436             out[0],
437             err_msg='Update SA key failed on {0}'.format(node['host']))
438
439     @staticmethod
440     def vpp_ipsec_add_spd(node, spd_id):
441         """Create Security Policy Database on the VPP node.
442
443         :param node: VPP node to add SPD on.
444         :param spd_id: SPD ID.
445         :type node: dict
446         :type spd_id: int
447         """
448         out = VatExecutor.cmd_from_template(node, 'ipsec/ipsec_spd_add.vat',
449                                             spd_id=spd_id)
450         VatJsonUtil.verify_vat_retval(
451             out[0],
452             err_msg='Add SPD {0} failed on {1}'.format(spd_id, node['host']))
453
454     @staticmethod
455     def vpp_ipsec_spd_add_if(node, spd_id, interface):
456         """Add interface to the Security Policy Database.
457
458         :param node: VPP node.
459         :param spd_id: SPD ID to add interface on.
460         :param interface: Interface name or sw_if_index.
461         :type node: dict
462         :type spd_id: int
463         :type interface: str or int
464         """
465         sw_if_index = Topology.get_interface_sw_index(node, interface)\
466             if isinstance(interface, basestring) else interface
467
468         out = VatExecutor.cmd_from_template(node,
469                                             'ipsec/ipsec_interface_add_spd.vat',
470                                             spd_id=spd_id, sw_if_id=sw_if_index)
471         VatJsonUtil.verify_vat_retval(
472             out[0],
473             err_msg='Add interface {0} to SPD {1} failed on {2}'.format(
474                 interface, spd_id, node['host']))
475
476     @staticmethod
477     def vpp_ipsec_policy_add(node, spd_id, priority, action, inbound=True,
478                              sa_id=None, laddr_range=None, raddr_range=None,
479                              proto=None, lport_range=None, rport_range=None,
480                              is_ipv6=False):
481         """Create Security Policy Database entry on the VPP node.
482
483         :param node: VPP node to add SPD entry on.
484         :param spd_id: SPD ID to add entry on.
485         :param priority: SPD entry priority, higher number = higher priority.
486         :param action: Policy action.
487         :param inbound: If True policy is for inbound traffic, otherwise
488             outbound.
489         :param sa_id: SAD entry ID for protect action.
490         :param laddr_range: Policy selector local IPv4 or IPv6 address range in
491             format IP/prefix or IP/mask. If no mask is provided,
492             it's considered to be /32.
493         :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
494             format IP/prefix or IP/mask. If no mask is provided,
495             it's considered to be /32.
496         :param proto: Policy selector next layer protocol number.
497         :param lport_range: Policy selector local TCP/UDP port range in format
498             <port_start>-<port_end>.
499         :param rport_range: Policy selector remote TCP/UDP port range in format
500             <port_start>-<port_end>.
501         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
502             not defined so it will default to address ::/0, otherwise False.
503         :type node: dict
504         :type spd_id: int
505         :type priority: int
506         :type action: PolicyAction
507         :type inbound: bool
508         :type sa_id: int
509         :type laddr_range: string
510         :type raddr_range: string
511         :type proto: int
512         :type lport_range: string
513         :type rport_range: string
514         :type is_ipv6: bool
515         """
516         direction = 'inbound' if inbound else 'outbound'
517
518         if laddr_range is None and is_ipv6:
519             laddr_range = '::/0'
520
521         if raddr_range is None and is_ipv6:
522             raddr_range = '::/0'
523
524         act_str = action.value
525         if PolicyAction.PROTECT == action and sa_id is not None:
526             act_str += ' sa {0}'.format(sa_id)
527
528         selector = ''
529         if laddr_range is not None:
530             net = ip_network(unicode(laddr_range), strict=False)
531             selector += 'local-ip-range {0} - {1} '.format(
532                 net.network_address, net.broadcast_address)
533         if raddr_range is not None:
534             net = ip_network(unicode(raddr_range), strict=False)
535             selector += 'remote-ip-range {0} - {1} '.format(
536                 net.network_address, net.broadcast_address)
537         if proto is not None:
538             selector += 'protocol {0} '.format(proto)
539         if lport_range is not None:
540             selector += 'local-port-range {0} '.format(lport_range)
541         if rport_range is not None:
542             selector += 'remote-port-range {0} '.format(rport_range)
543
544         out = VatExecutor.cmd_from_template(
545             node, 'ipsec/ipsec_policy_add.vat', json_param=False, spd_id=spd_id,
546             priority=priority, action=act_str, direction=direction,
547             selector=selector)
548         VatJsonUtil.verify_vat_retval(
549             out[0],
550             err_msg='Add IPsec policy ID {0} failed on {1}'.format(
551                 spd_id, node['host']))
552
553     @staticmethod
554     def vpp_ipsec_spd_add_entries(node, n_entries, spd_id, priority, inbound,
555                                   sa_id, raddr_ip, raddr_range):
556         """Create multiple Security Policy Database entries on the VPP node.
557
558         :param node: VPP node to add SPD entries on.
559         :param n_entries: Number of SPD entries to be added.
560         :param spd_id: SPD ID to add entries on.
561         :param priority: SPD entries priority, higher number = higher priority.
562         :param inbound: If True policy is for inbound traffic, otherwise
563             outbound.
564         :param sa_id: SAD entry ID for first entry. Each subsequent entry will
565             SAD entry ID incremented by 1.
566         :param raddr_ip: Policy selector remote IPv4 start address for the first
567             entry. Remote IPv4 end address will be calculated depending on
568             raddr_range parameter. Each subsequent entry will have start address
569             next after IPv4 end address of previous entry.
570         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
571             addresses. Valid values are from 1 to 32.
572         :type node: dict
573         :type n_entries: int
574         :type spd_id: int
575         :type priority: int
576         :type inbound: bool
577         :type sa_id: int
578         :type raddr_ip: string
579         :type raddr_range: int
580         """
581         tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.format(sa_id)
582
583         direction = 'inbound' if inbound else 'outbound'
584         addr_incr = 1 << (32 - raddr_range)
585         addr_ip = int(ip_address(unicode(raddr_ip)))
586         start_str = 'exec ipsec policy add spd {0} priority {1} {2} ' \
587                     'action protect sa'.format(spd_id, priority, direction)
588         with open(tmp_filename, 'w') as tmp_file:
589             for i in range(0, n_entries):
590                 r_ip_s = ip_address(addr_ip + addr_incr * i)
591                 r_ip_e = ip_address(addr_ip + addr_incr * (i+1) - 1)
592                 buf_str = '{0} {1} remote-ip-range {2} - {3}\n'.format(
593                     start_str, sa_id+i, r_ip_s, r_ip_e)
594                 tmp_file.write(buf_str)
595         vat = VatExecutor()
596         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
597                            copy_on_execute=True)
598         os.remove(tmp_filename)
599
600     @staticmethod
601     def vpp_ipsec_create_tunnel_interfaces(node1, node2, if1_ip_addr,
602                                            if2_ip_addr, if1_key, if2_key,
603                                            n_tunnels, crypto_alg, crypto_key,
604                                            integ_alg, integ_key, raddr_ip1,
605                                            raddr_ip2, raddr_range):
606         """Create multiple IPsec tunnel interfaces between two VPP nodes.
607
608         :param node1: VPP node 1 to create tunnel interfaces.
609         :param node2: VPP node 2 to create tunnel interfaces.
610         :param if1_ip_addr: VPP node 1 interface IP4 address.
611         :param if2_ip_addr: VPP node 2 interface IP4 address.
612         :param if1_key: VPP node 1 interface key from topology file.
613         :param if2_key: VPP node 2 interface key from topology file.
614         :param n_tunnels: Number of tunnell interfaces to create.
615         :param crypto_alg: The encryption algorithm name.
616         :param crypto_key: The encryption key string.
617         :param integ_alg: The integrity algorithm name.
618         :param integ_key: The integrity key string.
619         :param raddr_ip1: Policy selector remote IPv4 start address for the
620             first tunnel in direction node1->node2.
621         :param raddr_ip2: Policy selector remote IPv4 start address for the
622             first tunnel in direction node2->node1.
623         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
624             addresses. Valid values are from 1 to 32.
625         :type node1: dict
626         :type node2: dict
627         :type if1_ip_addr: str
628         :type if2_ip_addr: str
629         :type if1_key: str
630         :type if2_key: str
631         :type n_tunnels: int
632         :type crypto_alg: CryptoAlg
633         :type crypto_key: str
634         :type integ_alg: IntegAlg
635         :type integ_key: str
636         :type raddr_ip1: string
637         :type raddr_ip2: string
638         :type raddr_range: int
639         """
640         spi_1 = 10000
641         spi_2 = 20000
642
643         raddr_ip1_i = int(ip_address(unicode(raddr_ip1)))
644         raddr_ip2_i = int(ip_address(unicode(raddr_ip2)))
645         addr_incr = 1 << (32 - raddr_range)
646
647         tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
648         tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
649
650         ckey = crypto_key.encode('hex')
651         ikey = integ_key.encode('hex')
652
653         vat = VatExecutor()
654         with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
655             for i in range(0, n_tunnels):
656                 integ = ''
657                 if not crypto_alg.alg_name.startswith('aes-gcm-'):
658                     integ = 'integ_alg {integ_alg} '\
659                             'local_integ_key {local_integ_key} '\
660                             'remote_integ_key {remote_integ_key} '\
661                             .format(integ_alg=integ_alg.alg_name,
662                                     local_integ_key=ikey,
663                                     remote_integ_key=ikey)
664                 dut1_tunnel = 'ipsec_tunnel_if_add_del '\
665                               'local_spi {local_spi} '\
666                               'remote_spi {remote_spi} '\
667                               'crypto_alg {crypto_alg} '\
668                               'local_crypto_key {local_crypto_key} '\
669                               'remote_crypto_key {remote_crypto_key} '\
670                               '{integ} '\
671                               'local_ip {local_ip} '\
672                               'remote_ip {remote_ip}\n'\
673                               .format(local_spi=spi_1+i,
674                                       remote_spi=spi_2+i,
675                                       crypto_alg=crypto_alg.alg_name,
676                                       local_crypto_key=ckey,
677                                       remote_crypto_key=ckey,
678                                       integ=integ,
679                                       local_ip=if1_ip_addr,
680                                       remote_ip=if2_ip_addr)
681                 dut2_tunnel = 'ipsec_tunnel_if_add_del '\
682                               'local_spi {local_spi} '\
683                               'remote_spi {remote_spi} '\
684                               'crypto_alg {crypto_alg} '\
685                               'local_crypto_key {local_crypto_key} '\
686                               'remote_crypto_key {remote_crypto_key} '\
687                               '{integ} '\
688                               'local_ip {local_ip} '\
689                               'remote_ip {remote_ip}\n'\
690                               .format(local_spi=spi_2+i,
691                                       remote_spi=spi_1+i,
692                                       crypto_alg=crypto_alg.alg_name,
693                                       local_crypto_key=ckey,
694                                       remote_crypto_key=ckey,
695                                       integ=integ,
696                                       local_ip=if2_ip_addr,
697                                       remote_ip=if1_ip_addr)
698                 tmp_f1.write(dut1_tunnel)
699                 tmp_f2.write(dut2_tunnel)
700         vat.execute_script(tmp_fn1, node1, timeout=300, json_out=False,
701                            copy_on_execute=True)
702         vat.execute_script(tmp_fn2, node2, timeout=300, json_out=False,
703                            copy_on_execute=True)
704         os.remove(tmp_fn1)
705         os.remove(tmp_fn2)
706
707         with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
708             for i in range(0, n_tunnels):
709                 raddr_ip1 = ip_address(raddr_ip1_i + addr_incr*i)
710                 raddr_ip2 = ip_address(raddr_ip2_i + addr_incr*i)
711                 dut1_if = Topology.get_interface_name(node1, if1_key)
712                 dut1 = 'exec ip route add {raddr}/{mask} via {addr} ipsec{i}\n'\
713                        'exec set interface unnumbered ipsec{i} use {uifc}\n'\
714                        'exec set interface state ipsec{i} up\n'\
715                        .format(raddr=raddr_ip2, mask=raddr_range,
716                                addr=if2_ip_addr, i=i, uifc=dut1_if)
717                 dut2_if = Topology.get_interface_name(node2, if2_key)
718                 dut2 = 'exec ip route add {raddr}/{mask} via {addr} ipsec{i}\n'\
719                        'exec set interface unnumbered ipsec{i} use {uifc}\n'\
720                        'exec set interface state ipsec{i} up\n'\
721                        .format(raddr=raddr_ip1, mask=raddr_range,
722                                addr=if1_ip_addr, i=i, uifc=dut2_if)
723                 tmp_f1.write(dut1)
724                 tmp_f2.write(dut2)
725
726         vat.execute_script(tmp_fn1, node1, timeout=300, json_out=False,
727                            copy_on_execute=True)
728         vat.execute_script(tmp_fn2, node2, timeout=300, json_out=False,
729                            copy_on_execute=True)
730         os.remove(tmp_fn1)
731         os.remove(tmp_fn2)
732
733     @staticmethod
734     def vpp_ipsec_add_multiple_tunnels(node1, node2, interface1, interface2,
735                                        n_tunnels, crypto_alg, crypto_key,
736                                        integ_alg, integ_key, tunnel_ip1,
737                                        tunnel_ip2, raddr_ip1, raddr_ip2,
738                                        raddr_range):
739         """Create multiple IPsec tunnels between two VPP nodes.
740
741         :param node1: VPP node 1 to create tunnels.
742         :param node2: VPP node 2 to create tunnels.
743         :param interface1: Interface name or sw_if_index on node 1.
744         :param interface2: Interface name or sw_if_index on node 2.
745         :param n_tunnels: Number of tunnels to create.
746         :param crypto_alg: The encryption algorithm name.
747         :param crypto_key: The encryption key string.
748         :param integ_alg: The integrity algorithm name.
749         :param integ_key: The integrity key string.
750         :param tunnel_ip1: Tunnel node1 IPv4 address.
751         :param tunnel_ip2: Tunnel node2 IPv4 address.
752         :param raddr_ip1: Policy selector remote IPv4 start address for the
753             first tunnel in direction node1->node2.
754         :param raddr_ip2: Policy selector remote IPv4 start address for the
755             first tunnel in direction node2->node1.
756         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
757             addresses. Valid values are from 1 to 32.
758         :type node1: dict
759         :type node2: dict
760         :type interface1: str or int
761         :type interface2: str or int
762         :type n_tunnels: int
763         :type crypto_alg: CryptoAlg
764         :type crypto_key: str
765         :type integ_alg: str
766         :type integ_key: str
767         :type tunnel_ip1: str
768         :type tunnel_ip2: str
769         :type raddr_ip1: string
770         :type raddr_ip2: string
771         :type raddr_range: int
772         """
773         spd_id = 1
774         p_hi = 100
775         p_lo = 10
776         sa_id_1 = 10000
777         sa_id_2 = 20000
778         spi_1 = 30000
779         spi_2 = 40000
780         proto = 50
781
782         IPsecUtil.vpp_ipsec_add_spd(node1, spd_id)
783         IPsecUtil.vpp_ipsec_spd_add_if(node1, spd_id, interface1)
784         IPsecUtil.vpp_ipsec_policy_add(node1, spd_id, p_hi, PolicyAction.BYPASS,
785                                        inbound=False, proto=proto)
786         IPsecUtil.vpp_ipsec_policy_add(node1, spd_id, p_hi, PolicyAction.BYPASS,
787                                        inbound=True, proto=proto)
788
789         IPsecUtil.vpp_ipsec_add_spd(node2, spd_id)
790         IPsecUtil.vpp_ipsec_spd_add_if(node2, spd_id, interface2)
791         IPsecUtil.vpp_ipsec_policy_add(node2, spd_id, p_hi, PolicyAction.BYPASS,
792                                        inbound=False, proto=proto)
793         IPsecUtil.vpp_ipsec_policy_add(node2, spd_id, p_hi, PolicyAction.BYPASS,
794                                        inbound=True, 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)