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