VAT-to-PAPI: VPPCounters
[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_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 crypto_alg_aes_gcm_256():
140         """Return encryption algorithm aes-gcm-256.
141
142         :returns: CryptoAlg enum AES_GCM_128 object.
143         :rtype: CryptoAlg
144         """
145         return CryptoAlg.AES_GCM_256
146
147     @staticmethod
148     def get_crypto_alg_key_len(crypto_alg):
149         """Return encryption algorithm key length.
150
151         :param crypto_alg: Encryption algorithm.
152         :type crypto_alg: CryptoAlg
153         :returns: Key length.
154         :rtype: int
155         """
156         return crypto_alg.key_len
157
158     @staticmethod
159     def get_crypto_alg_scapy_name(crypto_alg):
160         """Return encryption algorithm scapy name.
161
162         :param crypto_alg: Encryption algorithm.
163         :type crypto_alg: CryptoAlg
164         :returns: Algorithm scapy name.
165         :rtype: str
166         """
167         return crypto_alg.scapy_name
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_512_256():
180         """Return integrity algorithm SHA-512-256.
181
182         :returns: IntegAlg enum SHA_512_256 object.
183         :rtype: IntegAlg
184         """
185         return IntegAlg.SHA_512_256
186
187     @staticmethod
188     def integ_alg_aes_gcm_128():
189         """Return integrity algorithm AES-GCM-128.
190
191         :returns: IntegAlg enum AES_GCM_128 object.
192         :rtype: IntegAlg
193         """
194         return IntegAlg.AES_GCM_128
195
196     @staticmethod
197     def integ_alg_aes_gcm_256():
198         """Return integrity algorithm AES-GCM-256.
199
200         :returns: IntegAlg enum AES_GCM_256 object.
201         :rtype: IntegAlg
202         """
203         return IntegAlg.AES_GCM_256
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         :raises RuntimeError: If failed to select IPsec backend or if no API
256             reply received.
257         """
258
259         cmd = 'ipsec_select_backend'
260         cmd_reply = 'ipsec_select_backend_reply'
261         err_msg = 'Failed to select IPsec backend on host {host}'.format(
262             host=node['host'])
263         args = dict(protocol=protocol, index=index)
264         with PapiExecutor(node) as papi_exec:
265             papi_resp = papi_exec.add(cmd, **args).execute_should_pass(err_msg)
266         data = papi_resp.reply[0]['api_reply'][cmd_reply]
267         if data['retval'] != 0:
268             raise RuntimeError('Failed to select IPsec backend on host {host}'.
269                                format(host=node['host']))
270
271     @staticmethod
272     def vpp_ipsec_backend_dump(node):
273         """Dump IPsec backends.
274
275         :param node: VPP node to dump IPsec backend on.
276         :type node: dict
277         """
278
279         err_msg = 'Failed to dump IPsec backends on host {host}'.format(
280             host=node['host'])
281         with PapiExecutor(node) as papi_exec:
282             papi_exec.add('ipsec_backend_dump').execute_should_pass(
283                 err_msg, process_reply=False)
284
285     @staticmethod
286     def vpp_ipsec_add_sad_entry(node, sad_id, spi, crypto_alg, crypto_key,
287                                 integ_alg, integ_key, tunnel_src=None,
288                                 tunnel_dst=None):
289         """Create Security Association Database entry on the VPP node.
290
291         :param node: VPP node to add SAD entry on.
292         :param sad_id: SAD entry ID.
293         :param spi: Security Parameter Index of this SAD entry.
294         :param crypto_alg: The encryption algorithm name.
295         :param crypto_key: The encryption key string.
296         :param integ_alg: The integrity algorithm name.
297         :param integ_key: The integrity key string.
298         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
299             specified ESP transport mode is used.
300         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
301             not specified ESP transport mode is used.
302         :type node: dict
303         :type sad_id: int
304         :type spi: int
305         :type crypto_alg: CryptoAlg
306         :type crypto_key: str
307         :type integ_alg: IntegAlg
308         :type integ_key: str
309         :type tunnel_src: str
310         :type tunnel_dst: str
311         """
312         ckey = crypto_key.encode('hex')
313         ikey = integ_key.encode('hex')
314         tunnel = 'tunnel-src {0} tunnel-dst {1}'.format(tunnel_src, tunnel_dst)\
315             if tunnel_src is not None and tunnel_dst is not None else ''
316
317         out = VatExecutor.cmd_from_template(node,
318                                             'ipsec/ipsec_sad_add_entry.vat',
319                                             sad_id=sad_id, spi=spi,
320                                             calg=crypto_alg.alg_name, ckey=ckey,
321                                             ialg=integ_alg.alg_name, ikey=ikey,
322                                             tunnel=tunnel)
323         VatJsonUtil.verify_vat_retval(
324             out[0],
325             err_msg='Add SAD entry failed on {0}'.format(node['host']))
326
327     @staticmethod
328     def vpp_ipsec_add_sad_entries(node, n_entries, sad_id, spi, crypto_alg,
329                                   crypto_key, integ_alg, integ_key,
330                                   tunnel_src=None, tunnel_dst=None):
331         """Create multiple Security Association Database entries on VPP node.
332
333         :param node: VPP node to add SAD entry on.
334         :param n_entries: Number of SAD entries to be created.
335         :param sad_id: First SAD entry ID. All subsequent SAD entries will have
336             id incremented by 1.
337         :param spi: Security Parameter Index of first SAD entry. All subsequent
338             SAD entries will have spi incremented by 1.
339         :param crypto_alg: The encryption algorithm name.
340         :param crypto_key: The encryption key string.
341         :param integ_alg: The integrity algorithm name.
342         :param integ_key: The integrity key string.
343         :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
344             specified ESP transport mode is used.
345         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
346             not specified ESP transport mode is used.
347         :type node: dict
348         :type n_entries: int
349         :type sad_id: int
350         :type spi: int
351         :type crypto_alg: CryptoAlg
352         :type crypto_key: str
353         :type integ_alg: IntegAlg
354         :type integ_key: str
355         :type tunnel_src: str
356         :type tunnel_dst: str
357         """
358         tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.format(sad_id)
359
360         addr_incr = 1 << (32 - 24)
361
362         with open(tmp_filename, 'w') as tmp_file:
363             for i in range(0, n_entries):
364                 integ = ''
365                 if not crypto_alg.alg_name.startswith('aes-gcm-'):
366                     integ = (
367                         'integ-alg {integ_alg} integ-key {integ_key}'.
368                         format(
369                             integ_alg=integ_alg.alg_name,
370                             integ_key=integ_key))
371                 tunnel = (
372                     'tunnel-src {laddr} tunnel-dst {raddr}'.
373                     format(
374                         laddr=ip_address(unicode(tunnel_src)) + i * addr_incr,
375                         raddr=ip_address(unicode(tunnel_dst)) + i * addr_incr)
376                     if tunnel_src and tunnel_dst is not None else '')
377                 conf = (
378                     'exec ipsec sa add {sad_id} esp spi {spi} '
379                     'crypto-alg {crypto_alg} crypto-key {crypto_key} '
380                     '{integ} {tunnel}\n'.
381                     format(
382                         sad_id=sad_id + i,
383                         spi=spi + i,
384                         crypto_alg=crypto_alg.alg_name,
385                         crypto_key=crypto_key,
386                         integ=integ,
387                         tunnel=tunnel))
388                 tmp_file.write(conf)
389         vat = VatExecutor()
390         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
391                            copy_on_execute=True)
392         os.remove(tmp_filename)
393
394     @staticmethod
395     def vpp_ipsec_set_ip_route(node, n_tunnels, tunnel_src, traffic_addr,
396                                tunnel_dst, interface, raddr_range):
397         """Set IP address and route on interface.
398
399         :param node: VPP node to add config on.
400         :param n_tunnels: Number of tunnels to create.
401         :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
402         :param traffic_addr: Traffic destination IP address to route.
403         :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
404         :param interface: Interface key on node 1.
405         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
406             addresses. Valid values are from 1 to 32.
407         :type node: dict
408         :type n_tunnels: int
409         :type tunnel_src: str
410         :type traffic_addr: str
411         :type tunnel_dst: str
412         :type interface: str
413         :type raddr_range: int
414         """
415         tmp_filename = '/tmp/ipsec_set_ip.script'
416
417         addr_incr = 1 << (32 - raddr_range)
418
419         with open(tmp_filename, 'w') as tmp_file:
420             for i in range(0, n_tunnels):
421                 conf = (
422                     'exec set interface ip address {interface} {laddr}/24\n'
423                     'exec ip route add {taddr}/32 via {raddr} {interface}\n'.
424                     format(
425                         interface=Topology.get_interface_name(node, interface),
426                         laddr=ip_address(unicode(tunnel_src)) + i * addr_incr,
427                         raddr=ip_address(unicode(tunnel_dst)) + i * addr_incr,
428                         taddr=ip_address(unicode(traffic_addr)) + i))
429                 tmp_file.write(conf)
430         vat = VatExecutor()
431         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
432                            copy_on_execute=True)
433         os.remove(tmp_filename)
434
435     @staticmethod
436     def vpp_ipsec_sa_set_key(node, sa_id, crypto_key, integ_key):
437         """Update Security Association (SA) keys.
438
439         :param node: VPP node to update SA keys.
440         :param sa_id: SAD entry ID.
441         :param crypto_key: The encryption key string.
442         :param integ_key: The integrity key string.
443         :type node: dict
444         :type sa_id: int
445         :type crypto_key: str
446         :type integ_key: str
447         """
448         ckey = crypto_key.encode('hex')
449         ikey = integ_key.encode('hex')
450
451         out = VatExecutor.cmd_from_template(
452             node, 'ipsec/ipsec_sa_set_key.vat', json_param=False, sa_id=sa_id,
453             ckey=ckey, ikey=ikey)
454         VatJsonUtil.verify_vat_retval(
455             out[0],
456             err_msg='Update SA key failed on {0}'.format(node['host']))
457
458     @staticmethod
459     def vpp_ipsec_add_spd(node, spd_id):
460         """Create Security Policy Database on the VPP node.
461
462         :param node: VPP node to add SPD on.
463         :param spd_id: SPD ID.
464         :type node: dict
465         :type spd_id: int
466         """
467         out = VatExecutor.cmd_from_template(node, 'ipsec/ipsec_spd_add.vat',
468                                             spd_id=spd_id)
469         VatJsonUtil.verify_vat_retval(
470             out[0],
471             err_msg='Add SPD {0} failed on {1}'.format(spd_id, node['host']))
472
473     @staticmethod
474     def vpp_ipsec_spd_add_if(node, spd_id, interface):
475         """Add interface to the Security Policy Database.
476
477         :param node: VPP node.
478         :param spd_id: SPD ID to add interface on.
479         :param interface: Interface name or sw_if_index.
480         :type node: dict
481         :type spd_id: int
482         :type interface: str or int
483         """
484         sw_if_index = Topology.get_interface_sw_index(node, interface)\
485             if isinstance(interface, basestring) else interface
486
487         out = VatExecutor.cmd_from_template(node,
488                                             'ipsec/ipsec_interface_add_spd.vat',
489                                             spd_id=spd_id, sw_if_id=sw_if_index)
490         VatJsonUtil.verify_vat_retval(
491             out[0],
492             err_msg='Add interface {0} to SPD {1} failed on {2}'.format(
493                 interface, spd_id, node['host']))
494
495     @staticmethod
496     def vpp_ipsec_policy_add(node, spd_id, priority, action, inbound=True,
497                              sa_id=None, laddr_range=None, raddr_range=None,
498                              proto=None, lport_range=None, rport_range=None,
499                              is_ipv6=False):
500         """Create Security Policy Database entry on the VPP node.
501
502         :param node: VPP node to add SPD entry on.
503         :param spd_id: SPD ID to add entry on.
504         :param priority: SPD entry priority, higher number = higher priority.
505         :param action: Policy action.
506         :param inbound: If True policy is for inbound traffic, otherwise
507             outbound.
508         :param sa_id: SAD entry ID for protect action.
509         :param laddr_range: Policy selector local IPv4 or IPv6 address range in
510             format IP/prefix or IP/mask. If no mask is provided,
511             it's considered to be /32.
512         :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
513             format IP/prefix or IP/mask. If no mask is provided,
514             it's considered to be /32.
515         :param proto: Policy selector next layer protocol number.
516         :param lport_range: Policy selector local TCP/UDP port range in format
517             <port_start>-<port_end>.
518         :param rport_range: Policy selector remote TCP/UDP port range in format
519             <port_start>-<port_end>.
520         :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
521             not defined so it will default to address ::/0, otherwise False.
522         :type node: dict
523         :type spd_id: int
524         :type priority: int
525         :type action: PolicyAction
526         :type inbound: bool
527         :type sa_id: int
528         :type laddr_range: string
529         :type raddr_range: string
530         :type proto: int
531         :type lport_range: string
532         :type rport_range: string
533         :type is_ipv6: bool
534         """
535         direction = 'inbound' if inbound else 'outbound'
536
537         if laddr_range is None and is_ipv6:
538             laddr_range = '::/0'
539
540         if raddr_range is None and is_ipv6:
541             raddr_range = '::/0'
542
543         act_str = action.value
544         if PolicyAction.PROTECT == action and sa_id is not None:
545             act_str += ' sa {0}'.format(sa_id)
546
547         selector = ''
548         if laddr_range is not None:
549             net = ip_network(unicode(laddr_range), strict=False)
550             selector += 'local-ip-range {0} - {1} '.format(
551                 net.network_address, net.broadcast_address)
552         if raddr_range is not None:
553             net = ip_network(unicode(raddr_range), strict=False)
554             selector += 'remote-ip-range {0} - {1} '.format(
555                 net.network_address, net.broadcast_address)
556         if proto is not None:
557             selector += 'protocol {0} '.format(proto)
558         if lport_range is not None:
559             selector += 'local-port-range {0} '.format(lport_range)
560         if rport_range is not None:
561             selector += 'remote-port-range {0} '.format(rport_range)
562
563         out = VatExecutor.cmd_from_template(
564             node, 'ipsec/ipsec_policy_add.vat', json_param=False, spd_id=spd_id,
565             priority=priority, action=act_str, direction=direction,
566             selector=selector)
567         VatJsonUtil.verify_vat_retval(
568             out[0],
569             err_msg='Add IPsec policy ID {0} failed on {1}'.format(
570                 spd_id, node['host']))
571
572     @staticmethod
573     def vpp_ipsec_spd_add_entries(node, n_entries, spd_id, priority, inbound,
574                                   sa_id, raddr_ip):
575         """Create multiple Security Policy Database entries on the VPP node.
576
577         :param node: VPP node to add SPD entries on.
578         :param n_entries: Number of SPD entries to be added.
579         :param spd_id: SPD ID to add entries on.
580         :param priority: SPD entries priority, higher number = higher priority.
581         :param inbound: If True policy is for inbound traffic, otherwise
582             outbound.
583         :param sa_id: SAD entry ID for first entry. Each subsequent entry will
584             SAD entry ID incremented by 1.
585         :param raddr_ip: Policy selector remote IPv4 start address for the first
586             entry. Remote IPv4 end address will be calculated depending on
587             raddr_range parameter. Each subsequent entry will have start address
588             next after IPv4 end address of previous entry.
589         :type node: dict
590         :type n_entries: int
591         :type spd_id: int
592         :type priority: int
593         :type inbound: bool
594         :type sa_id: int
595         :type raddr_ip: string
596         """
597         tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.format(sa_id)
598
599         with open(tmp_filename, 'w') as tmp_file:
600             for i in range(0, n_entries):
601                 raddr_s = ip_address(unicode(raddr_ip)) + i
602                 raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1
603                 tunnel = (
604                     'exec ipsec policy add spd {spd_id} priority {priority} '
605                     '{direction} action protect sa {sa_id} '
606                     'remote-ip-range {raddr_s} - {raddr_e} '
607                     'local-ip-range 0.0.0.0 - 255.255.255.255\n'.
608                     format(
609                         spd_id=spd_id,
610                         priority=priority,
611                         direction='inbound' if inbound else 'outbound',
612                         sa_id=sa_id+i,
613                         raddr_s=raddr_s,
614                         raddr_e=raddr_e))
615                 tmp_file.write(tunnel)
616         vat = VatExecutor()
617         vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
618                            copy_on_execute=True)
619         os.remove(tmp_filename)
620
621     @staticmethod
622     def vpp_ipsec_create_tunnel_interfaces(nodes, if1_ip_addr, if2_ip_addr,
623                                            if1_key, if2_key, n_tunnels,
624                                            crypto_alg, integ_alg, raddr_ip1,
625                                            raddr_ip2, raddr_range):
626         """Create multiple IPsec tunnel interfaces between two VPP nodes.
627
628         :param nodes: VPP nodes to create tunnel interfaces.
629         :param if1_ip_addr: VPP node 1 interface IP4 address.
630         :param if2_ip_addr: VPP node 2 interface IP4 address.
631         :param if1_key: VPP node 1 interface key from topology file.
632         :param if2_key: VPP node 2 interface key from topology file.
633         :param n_tunnels: Number of tunnell interfaces to create.
634         :param crypto_alg: The encryption algorithm name.
635         :param integ_alg: The integrity algorithm name.
636         :param raddr_ip1: Policy selector remote IPv4 start address for the
637             first tunnel in direction node1->node2.
638         :param raddr_ip2: Policy selector remote IPv4 start address for the
639             first tunnel in direction node2->node1.
640         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
641             addresses. Valid values are from 1 to 32.
642         :type nodes: dict
643         :type if1_ip_addr: str
644         :type if2_ip_addr: str
645         :type if1_key: str
646         :type if2_key: str
647         :type n_tunnels: int
648         :type crypto_alg: CryptoAlg
649         :type integ_alg: IntegAlg
650         :type raddr_ip1: string
651         :type raddr_ip2: string
652         :type raddr_range: int
653         """
654         spi_1 = 100000
655         spi_2 = 200000
656         addr_incr = 1 << (32 - raddr_range)
657
658         tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
659         tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
660
661         vat = VatExecutor()
662
663         with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
664             for i in range(0, n_tunnels):
665                 ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
666                 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
667                 integ = ''
668                 if not crypto_alg.alg_name.startswith('aes-gcm-'):
669                     integ = (
670                         'integ_alg {integ_alg} '
671                         'local_integ_key {local_integ_key} '
672                         'remote_integ_key {remote_integ_key} '
673                         .format(
674                             integ_alg=integ_alg.alg_name,
675                             local_integ_key=ikey,
676                             remote_integ_key=ikey))
677                 tmp_f1.write(
678                     'exec set interface ip address {uifc} {laddr}/24\n'
679                     'ipsec_tunnel_if_add_del '
680                     'local_spi {local_spi} '
681                     'remote_spi {remote_spi} '
682                     'crypto_alg {crypto_alg} '
683                     'local_crypto_key {local_crypto_key} '
684                     'remote_crypto_key {remote_crypto_key} '
685                     '{integ} '
686                     'local_ip {laddr} '
687                     'remote_ip {raddr}\n'
688                     .format(
689                         local_spi=spi_1 + i,
690                         remote_spi=spi_2 + i,
691                         crypto_alg=crypto_alg.alg_name,
692                         local_crypto_key=ckey,
693                         remote_crypto_key=ckey,
694                         integ=integ,
695                         laddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr,
696                         raddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr,
697                         uifc=Topology.get_interface_name(nodes['DUT1'],
698                                                          if1_key)))
699                 tmp_f2.write(
700                     'exec set interface ip address {uifc} {laddr}/24\n'
701                     'ipsec_tunnel_if_add_del '
702                     'local_spi {local_spi} '
703                     'remote_spi {remote_spi} '
704                     'crypto_alg {crypto_alg} '
705                     'local_crypto_key {local_crypto_key} '
706                     'remote_crypto_key {remote_crypto_key} '
707                     '{integ} '
708                     'local_ip {laddr} '
709                     'remote_ip {raddr}\n'
710                     .format(
711                         local_spi=spi_2 + i,
712                         remote_spi=spi_1 + i,
713                         crypto_alg=crypto_alg.alg_name,
714                         local_crypto_key=ckey,
715                         remote_crypto_key=ckey,
716                         integ=integ,
717                         laddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr,
718                         raddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr,
719                         uifc=Topology.get_interface_name(nodes['DUT2'],
720                                                          if2_key)))
721         vat.execute_script(tmp_fn1, nodes['DUT1'], timeout=300, json_out=False,
722                            copy_on_execute=True)
723         vat.execute_script(tmp_fn2, nodes['DUT2'], timeout=300, json_out=False,
724                            copy_on_execute=True)
725         os.remove(tmp_fn1)
726         os.remove(tmp_fn2)
727
728         with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
729             for i in range(0, n_tunnels):
730                 tmp_f1.write(
731                     'exec set interface unnumbered ipsec{i} use {uifc}\n'
732                     'exec set interface state ipsec{i} up\n'
733                     'exec ip route add {taddr}/32 via {raddr} ipsec{i}\n'
734                     .format(
735                         taddr=ip_address(unicode(raddr_ip2)) + i,
736                         raddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr,
737                         i=i,
738                         uifc=Topology.get_interface_name(nodes['DUT1'],
739                                                          if1_key)))
740                 tmp_f2.write(
741                     'exec set interface unnumbered ipsec{i} use {uifc}\n'
742                     'exec set interface state ipsec{i} up\n'
743                     'exec ip route add {taddr}/32 via {raddr} ipsec{i}\n'
744                     .format(
745                         taddr=ip_address(unicode(raddr_ip1)) + i,
746                         raddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr,
747                         i=i,
748                         uifc=Topology.get_interface_name(nodes['DUT2'],
749                                                          if2_key)))
750         vat.execute_script(tmp_fn1, nodes['DUT1'], timeout=300, json_out=False,
751                            copy_on_execute=True)
752         vat.execute_script(tmp_fn2, nodes['DUT2'], timeout=300, json_out=False,
753                            copy_on_execute=True)
754         os.remove(tmp_fn1)
755         os.remove(tmp_fn2)
756
757     @staticmethod
758     def vpp_ipsec_add_multiple_tunnels(nodes, interface1, interface2,
759                                        n_tunnels, crypto_alg, integ_alg,
760                                        tunnel_ip1, tunnel_ip2, raddr_ip1,
761                                        raddr_ip2, raddr_range):
762         """Create multiple IPsec tunnels between two VPP nodes.
763
764         :param nodes: VPP nodes to create tunnels.
765         :param interface1: Interface name or sw_if_index on node 1.
766         :param interface2: Interface name or sw_if_index on node 2.
767         :param n_tunnels: Number of tunnels to create.
768         :param crypto_alg: The encryption algorithm name.
769         :param integ_alg: The integrity algorithm name.
770         :param tunnel_ip1: Tunnel node1 IPv4 address.
771         :param tunnel_ip2: Tunnel node2 IPv4 address.
772         :param raddr_ip1: Policy selector remote IPv4 start address for the
773             first tunnel in direction node1->node2.
774         :param raddr_ip2: Policy selector remote IPv4 start address for the
775             first tunnel in direction node2->node1.
776         :param raddr_range: Mask specifying range of Policy selector Remote IPv4
777             addresses. Valid values are from 1 to 32.
778         :type nodes: dict
779         :type interface1: str or int
780         :type interface2: str or int
781         :type n_tunnels: int
782         :type crypto_alg: CryptoAlg
783         :type integ_alg: str
784         :type tunnel_ip1: str
785         :type tunnel_ip2: str
786         :type raddr_ip1: string
787         :type raddr_ip2: string
788         :type raddr_range: int
789         """
790         spd_id = 1
791         p_hi = 100
792         p_lo = 10
793         sa_id_1 = 100000
794         sa_id_2 = 200000
795         spi_1 = 300000
796         spi_2 = 400000
797
798         crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
799         integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
800
801         IPsecUtil.vpp_ipsec_set_ip_route(
802             nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
803             interface1, raddr_range)
804         IPsecUtil.vpp_ipsec_set_ip_route(
805             nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
806             interface2, raddr_range)
807
808         IPsecUtil.vpp_ipsec_add_spd(
809             nodes['DUT1'], spd_id)
810         IPsecUtil.vpp_ipsec_spd_add_if(
811             nodes['DUT1'], spd_id, interface1)
812         IPsecUtil.vpp_ipsec_policy_add(
813             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
814             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
815         IPsecUtil.vpp_ipsec_policy_add(
816             nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
817             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
818
819         IPsecUtil.vpp_ipsec_add_spd(
820             nodes['DUT2'], spd_id)
821         IPsecUtil.vpp_ipsec_spd_add_if(
822             nodes['DUT2'], spd_id, interface2)
823         IPsecUtil.vpp_ipsec_policy_add(
824             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
825             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
826         IPsecUtil.vpp_ipsec_policy_add(
827             nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
828             proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
829
830         IPsecUtil.vpp_ipsec_add_sad_entries(
831             nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
832             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
833
834         IPsecUtil.vpp_ipsec_spd_add_entries(
835             nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
836
837         IPsecUtil.vpp_ipsec_add_sad_entries(
838             nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
839             integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
840
841         IPsecUtil.vpp_ipsec_spd_add_entries(
842             nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
843
844         IPsecUtil.vpp_ipsec_add_sad_entries(
845             nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
846             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
847
848         IPsecUtil.vpp_ipsec_spd_add_entries(
849             nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
850
851         IPsecUtil.vpp_ipsec_add_sad_entries(
852             nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
853             integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
854
855         IPsecUtil.vpp_ipsec_spd_add_entries(
856             nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
857
858     @staticmethod
859     def vpp_ipsec_show(node):
860         """Run "show ipsec" debug CLI command.
861
862         :param node: Node to run command on.
863         :type node: dict
864         """
865         VatExecutor().execute_script('ipsec/ipsec_show.vat', node,
866                                      json_out=False)