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