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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """IPsec utilities library."""
18 from random import choice
19 from string import letters
21 from enum import Enum, IntEnum
22 from ipaddress import ip_network, ip_address
24 from resources.libraries.python.Constants import Constants
25 from resources.libraries.python.IPUtil import IPUtil
26 from resources.libraries.python.InterfaceUtil import InterfaceUtil
27 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
28 from resources.libraries.python.topology import Topology
29 from resources.libraries.python.VatExecutor import VatExecutor
33 """Generate random string as a key.
35 :param length: Length of generated payload.
37 :returns: The generated payload.
40 return ''.join(choice(letters) for _ in range(length))
43 class PolicyAction(Enum):
45 BYPASS = ('bypass', 0)
46 DISCARD = ('discard', 1)
47 PROTECT = ('protect', 3)
49 def __init__(self, policy_name, policy_int_repr):
50 self.policy_name = policy_name
51 self.policy_int_repr = policy_int_repr
54 class CryptoAlg(Enum):
55 """Encryption algorithms."""
56 AES_CBC_128 = ('aes-cbc-128', 1, 'AES-CBC', 16)
57 AES_CBC_256 = ('aes-cbc-256', 3, 'AES-CBC', 32)
58 AES_GCM_128 = ('aes-gcm-128', 7, 'AES-GCM', 16)
59 AES_GCM_256 = ('aes-gcm-256', 9, 'AES-GCM', 32)
61 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
62 self.alg_name = alg_name
63 self.alg_int_repr = alg_int_repr
64 self.scapy_name = scapy_name
65 self.key_len = key_len
69 """Integrity algorithm."""
70 SHA_256_128 = ('sha-256-128', 4, 'SHA2-256-128', 32)
71 SHA_512_256 = ('sha-512-256', 6, 'SHA2-512-256', 64)
73 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
74 self.alg_name = alg_name
75 self.alg_int_repr = alg_int_repr
76 self.scapy_name = scapy_name
77 self.key_len = key_len
80 class IPsecProto(IntEnum):
86 class IPsecSadFlags(IntEnum):
87 """IPsec Security Association Database flags."""
88 IPSEC_API_SAD_FLAG_NONE = 0
89 IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
90 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
93 class IPsecUtil(object):
94 """IPsec utilities."""
97 def policy_action_bypass():
98 """Return policy action bypass.
100 :returns: PolicyAction enum BYPASS object.
103 return PolicyAction.BYPASS
106 def policy_action_discard():
107 """Return policy action discard.
109 :returns: PolicyAction enum DISCARD object.
112 return PolicyAction.DISCARD
115 def policy_action_protect():
116 """Return policy action protect.
118 :returns: PolicyAction enum PROTECT object.
121 return PolicyAction.PROTECT
124 def crypto_alg_aes_cbc_128():
125 """Return encryption algorithm aes-cbc-128.
127 :returns: CryptoAlg enum AES_CBC_128 object.
130 return CryptoAlg.AES_CBC_128
133 def crypto_alg_aes_cbc_256():
134 """Return encryption algorithm aes-cbc-256.
136 :returns: CryptoAlg enum AES_CBC_256 object.
139 return CryptoAlg.AES_CBC_256
142 def crypto_alg_aes_gcm_128():
143 """Return encryption algorithm aes-gcm-128.
145 :returns: CryptoAlg enum AES_GCM_128 object.
148 return CryptoAlg.AES_GCM_128
151 def crypto_alg_aes_gcm_256():
152 """Return encryption algorithm aes-gcm-256.
154 :returns: CryptoAlg enum AES_GCM_128 object.
157 return CryptoAlg.AES_GCM_256
160 def get_crypto_alg_key_len(crypto_alg):
161 """Return encryption algorithm key length.
163 :param crypto_alg: Encryption algorithm.
164 :type crypto_alg: CryptoAlg
165 :returns: Key length.
168 return crypto_alg.key_len
171 def get_crypto_alg_scapy_name(crypto_alg):
172 """Return encryption algorithm scapy name.
174 :param crypto_alg: Encryption algorithm.
175 :type crypto_alg: CryptoAlg
176 :returns: Algorithm scapy name.
179 return crypto_alg.scapy_name
182 def integ_alg_sha_256_128():
183 """Return integrity algorithm SHA-256-128.
185 :returns: IntegAlg enum SHA_256_128 object.
188 return IntegAlg.SHA_256_128
191 def integ_alg_sha_512_256():
192 """Return integrity algorithm SHA-512-256.
194 :returns: IntegAlg enum SHA_512_256 object.
197 return IntegAlg.SHA_512_256
200 def get_integ_alg_key_len(integ_alg):
201 """Return integrity algorithm key length.
203 :param integ_alg: Integrity algorithm.
204 :type integ_alg: IntegAlg
205 :returns: Key length.
208 return integ_alg.key_len
211 def get_integ_alg_scapy_name(integ_alg):
212 """Return integrity algorithm scapy name.
214 :param integ_alg: Integrity algorithm.
215 :type integ_alg: IntegAlg
216 :returns: Algorithm scapy name.
219 return integ_alg.scapy_name
222 def ipsec_proto_esp():
223 """Return IPSec protocol ESP.
225 :returns: IPsecProto enum ESP object.
228 return int(IPsecProto.ESP)
231 def ipsec_proto_ah():
232 """Return IPSec protocol AH.
234 :returns: IPsecProto enum AH object.
237 return int(IPsecProto.SEC_AH)
240 def vpp_ipsec_select_backend(node, protocol, index=1):
241 """Select IPsec backend.
243 :param node: VPP node to select IPsec backend on.
244 :param protocol: IPsec protocol.
245 :param index: Backend index.
247 :type protocol: IPsecProto
249 :raises RuntimeError: If failed to select IPsec backend or if no API
252 cmd = 'ipsec_select_backend'
253 err_msg = 'Failed to select IPsec backend on host {host}'.format(
259 with PapiSocketExecutor(node) as papi_exec:
260 papi_exec.add(cmd, **args).get_reply(err_msg)
263 def vpp_ipsec_backend_dump(node):
264 """Dump IPsec backends.
266 :param node: VPP node to dump IPsec backend on.
269 err_msg = 'Failed to dump IPsec backends on host {host}'.format(
271 with PapiSocketExecutor(node) as papi_exec:
272 papi_exec.add('ipsec_backend_dump').get_details(err_msg)
275 def vpp_ipsec_add_sad_entry(
276 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
277 integ_key='', tunnel_src=None, tunnel_dst=None):
278 """Create Security Association Database entry on the VPP node.
280 :param node: VPP node to add SAD entry on.
281 :param sad_id: SAD entry ID.
282 :param spi: Security Parameter Index of this SAD entry.
283 :param crypto_alg: The encryption algorithm name.
284 :param crypto_key: The encryption key string.
285 :param integ_alg: The integrity algorithm name.
286 :param integ_key: The integrity key string.
287 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
288 specified ESP transport mode is used.
289 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
290 not specified ESP transport mode is used.
294 :type crypto_alg: CryptoAlg
295 :type crypto_key: str
296 :type integ_alg: IntegAlg
298 :type tunnel_src: str
299 :type tunnel_dst: str
302 length=len(crypto_key),
306 length=len(integ_key),
307 data=integ_key if integ_key else 0
310 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
311 if tunnel_src and tunnel_dst:
312 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
313 src_addr = ip_address(unicode(tunnel_src))
314 dst_addr = ip_address(unicode(tunnel_dst))
315 if src_addr.version == 6:
317 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
322 cmd = 'ipsec_sad_entry_add_del'
323 err_msg = 'Failed to add Security Association Database entry on ' \
324 'host {host}'.format(host=node['host'])
328 crypto_algorithm=crypto_alg.alg_int_repr,
330 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
333 tunnel_src=str(src_addr),
334 tunnel_dst=str(dst_addr),
335 protocol=int(IPsecProto.ESP)
341 with PapiSocketExecutor(node) as papi_exec:
342 papi_exec.add(cmd, **args).get_reply(err_msg)
345 def vpp_ipsec_add_sad_entries(
346 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
347 integ_alg=None, integ_key='', tunnel_src=None, tunnel_dst=None):
348 """Create multiple Security Association Database entries on VPP node.
350 :param node: VPP node to add SAD entry on.
351 :param n_entries: Number of SAD entries to be created.
352 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
354 :param spi: Security Parameter Index of first SAD entry. All subsequent
355 SAD entries will have spi incremented by 1.
356 :param crypto_alg: The encryption algorithm name.
357 :param crypto_key: The encryption key string.
358 :param integ_alg: The integrity algorithm name.
359 :param integ_key: The integrity key string.
360 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
361 specified ESP transport mode is used.
362 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
363 not specified ESP transport mode is used.
368 :type crypto_alg: CryptoAlg
369 :type crypto_key: str
370 :type integ_alg: IntegAlg
372 :type tunnel_src: str
373 :type tunnel_dst: str
375 if tunnel_src and tunnel_dst:
376 src_addr = ip_address(unicode(tunnel_src))
377 dst_addr = ip_address(unicode(tunnel_dst))
382 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
385 if int(n_entries) > 10:
386 tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.\
389 with open(tmp_filename, 'w') as tmp_file:
390 for i in xrange(n_entries):
392 'integ-alg {integ_alg} integ-key {integ_key}'.format(
393 integ_alg=integ_alg.alg_name,
394 integ_key=integ_key.encode('hex'))
395 if integ_alg else '')
397 'tunnel-src {laddr} tunnel-dst {raddr}'.format(
398 laddr=src_addr + i * addr_incr,
399 raddr=dst_addr + i * addr_incr)
400 if tunnel_src and tunnel_dst else '')
402 'exec ipsec sa add {sad_id} esp spi {spi} '
403 'crypto-alg {crypto_alg} crypto-key {crypto_key} '
404 '{integ} {tunnel}\n'.format(
407 crypto_alg=crypto_alg.alg_name,
408 crypto_key=crypto_key.encode('hex'),
413 vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
414 copy_on_execute=True)
415 os.remove(tmp_filename)
419 length=len(crypto_key),
423 length=len(integ_key),
424 data=integ_key if integ_key else 0
427 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
428 if tunnel_src and tunnel_dst:
429 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
430 if src_addr.version == 6:
432 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
434 cmd = 'ipsec_sad_entry_add_del'
435 err_msg = 'Failed to add Security Association Database entry on ' \
436 'host {host}'.format(host=node['host'])
441 crypto_algorithm=crypto_alg.alg_int_repr,
443 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
446 tunnel_src=str(src_addr),
447 tunnel_dst=str(dst_addr),
448 protocol=int(IPsecProto.ESP)
454 with PapiSocketExecutor(node) as papi_exec:
455 for i in xrange(n_entries):
456 args['entry']['sad_id'] = int(sad_id) + i
457 args['entry']['spi'] = int(spi) + i
458 args['entry']['tunnel_src'] = str(src_addr + i * addr_incr) \
459 if tunnel_src and tunnel_dst else src_addr
460 args['entry']['tunnel_dst'] = str(dst_addr + i * addr_incr) \
461 if tunnel_src and tunnel_dst else dst_addr
462 history = False if 1 < i < n_entries - 1 else True
463 papi_exec.add(cmd, history=history, **args)
464 if i > 0 and i % Constants.PAPI_MAX_API_BULK == 0:
465 papi_exec.get_replies(err_msg)
466 papi_exec.get_replies(err_msg)
469 def vpp_ipsec_set_ip_route(
470 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
472 """Set IP address and route on interface.
474 :param node: VPP node to add config on.
475 :param n_tunnels: Number of tunnels to create.
476 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
477 :param traffic_addr: Traffic destination IP address to route.
478 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
479 :param interface: Interface key on node 1.
480 :param raddr_range: Mask specifying range of Policy selector Remote IP
481 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
485 :type tunnel_src: str
486 :type traffic_addr: str
487 :type tunnel_dst: str
489 :type raddr_range: int
491 laddr = ip_address(unicode(tunnel_src))
492 raddr = ip_address(unicode(tunnel_dst))
493 taddr = ip_address(unicode(traffic_addr))
494 addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
495 else 1 << (32 - raddr_range)
497 if int(n_tunnels) > 10:
498 tmp_filename = '/tmp/ipsec_set_ip.script'
500 with open(tmp_filename, 'w') as tmp_file:
501 for i in xrange(n_tunnels):
503 'exec set interface ip address {interface} '
504 '{laddr}/{laddr_l}\n'
505 'exec ip route add {taddr}/{taddr_l} via {raddr} '
506 '{interface}\n'.format(
507 interface=Topology.get_interface_name(
509 laddr=laddr + i * addr_incr,
511 raddr=raddr + i * addr_incr,
513 taddr_l=128 if taddr.version == 6 else 32))
516 vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
517 copy_on_execute=True)
518 os.remove(tmp_filename)
521 cmd1 = 'sw_interface_add_del_address'
523 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
525 is_ipv6=1 if laddr.version == 6 else 0,
527 address_length=raddr_range,
530 cmd2 = 'ip_route_add_del'
531 route = IPUtil.compose_vpp_route_structure(
533 prefix_len=128 if taddr.version == 6 else 32,
542 err_msg = 'Failed to configure IP addresses and IP routes on ' \
543 'interface {ifc} on host {host}'.\
544 format(ifc=interface, host=node['host'])
546 with PapiSocketExecutor(node) as papi_exec:
547 for i in xrange(n_tunnels):
548 args1['address'] = getattr(laddr + i * addr_incr, 'packed')
549 args2['route']['prefix']['address']['un'] = \
550 IPUtil.union_addr(taddr + i)
551 args2['route']['paths'][0]['nh']['address'] = \
552 IPUtil.union_addr(raddr + i * addr_incr)
553 history = False if 1 < i < n_tunnels - 1 else True
554 papi_exec.add(cmd1, history=history, **args1).\
555 add(cmd2, history=history, **args2)
556 if i > 0 and i % Constants.PAPI_MAX_API_BULK / 2 == 0:
557 papi_exec.get_replies(err_msg)
558 papi_exec.get_replies(err_msg)
561 def vpp_ipsec_add_spd(node, spd_id):
562 """Create Security Policy Database on the VPP node.
564 :param node: VPP node to add SPD on.
565 :param spd_id: SPD ID.
569 cmd = 'ipsec_spd_add_del'
570 err_msg = 'Failed to add Security Policy Database on host {host}'.\
571 format(host=node['host'])
576 with PapiSocketExecutor(node) as papi_exec:
577 papi_exec.add(cmd, **args).get_reply(err_msg)
580 def vpp_ipsec_spd_add_if(node, spd_id, interface):
581 """Add interface to the Security Policy Database.
583 :param node: VPP node.
584 :param spd_id: SPD ID to add interface on.
585 :param interface: Interface name or sw_if_index.
588 :type interface: str or int
590 cmd = 'ipsec_interface_add_del_spd'
591 err_msg = 'Failed to add interface {ifc} to Security Policy Database ' \
592 '{spd} on host {host}'.\
593 format(ifc=interface, spd=spd_id, host=node['host'])
596 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
599 with PapiSocketExecutor(node) as papi_exec:
600 papi_exec.add(cmd, **args).get_reply(err_msg)
603 def vpp_ipsec_policy_add(
604 node, spd_id, priority, action, inbound=True, sa_id=None,
605 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
606 rport_range=None, is_ipv6=False):
607 """Create Security Policy Database entry on the VPP node.
609 :param node: VPP node to add SPD entry on.
610 :param spd_id: SPD ID to add entry on.
611 :param priority: SPD entry priority, higher number = higher priority.
612 :param action: Policy action.
613 :param inbound: If True policy is for inbound traffic, otherwise
615 :param sa_id: SAD entry ID for protect action.
616 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
617 format IP/prefix or IP/mask. If no mask is provided,
618 it's considered to be /32.
619 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
620 format IP/prefix or IP/mask. If no mask is provided,
621 it's considered to be /32.
622 :param proto: Policy selector next layer protocol number.
623 :param lport_range: Policy selector local TCP/UDP port range in format
624 <port_start>-<port_end>.
625 :param rport_range: Policy selector remote TCP/UDP port range in format
626 <port_start>-<port_end>.
627 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
628 not defined so it will default to address ::/0, otherwise False.
632 :type action: PolicyAction
635 :type laddr_range: string
636 :type raddr_range: string
638 :type lport_range: string
639 :type rport_range: string
643 if laddr_range is None:
644 laddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
646 if raddr_range is None:
647 raddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
649 cmd = 'ipsec_spd_entry_add_del'
650 err_msg = 'Failed to add entry to Security Policy Database ' \
651 '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
655 priority=int(priority),
656 is_outbound=0 if inbound else 1,
657 sa_id=int(sa_id) if sa_id else 0,
658 policy=action.policy_int_repr,
659 protocol=int(proto) if proto else 0,
660 remote_address_start=IPUtil.create_ip_address_object(
661 ip_network(unicode(raddr_range), strict=False).network_address),
662 remote_address_stop=IPUtil.create_ip_address_object(
664 unicode(raddr_range), strict=False).broadcast_address),
665 local_address_start=IPUtil.create_ip_address_object(
667 unicode(laddr_range), strict=False).network_address),
668 local_address_stop=IPUtil.create_ip_address_object(
670 unicode(laddr_range), strict=False).broadcast_address),
671 remote_port_start=int(rport_range.split('-')[0]) if rport_range
673 remote_port_stop=int(rport_range.split('-')[1]) if rport_range
675 local_port_start=int(lport_range.split('-')[0]) if lport_range
677 local_port_stop=int(lport_range.split('-')[1]) if rport_range
684 with PapiSocketExecutor(node) as papi_exec:
685 papi_exec.add(cmd, **args).get_reply(err_msg)
688 def vpp_ipsec_spd_add_entries(
689 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip):
690 """Create multiple Security Policy Database entries on the VPP node.
692 :param node: VPP node to add SPD entries on.
693 :param n_entries: Number of SPD entries to be added.
694 :param spd_id: SPD ID to add entries on.
695 :param priority: SPD entries priority, higher number = higher priority.
696 :param inbound: If True policy is for inbound traffic, otherwise
698 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
699 SAD entry ID incremented by 1.
700 :param raddr_ip: Policy selector remote IPv4 start address for the first
701 entry. Remote IPv4 end address will be calculated depending on
702 raddr_range parameter. Each subsequent entry will have start address
703 next after IPv4 end address of previous entry.
710 :type raddr_ip: string
712 if int(n_entries) > 10:
713 tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.\
716 with open(tmp_filename, 'w') as tmp_file:
717 for i in xrange(n_entries):
718 raddr_s = ip_address(unicode(raddr_ip)) + i
719 raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1
721 'exec ipsec policy add spd {spd_id} '
722 'priority {priority} {direction} action protect '
723 'sa {sa_id} remote-ip-range {raddr_s} - {raddr_e} '
724 'local-ip-range 0.0.0.0 - 255.255.255.255\n'.
728 direction='inbound' if inbound else 'outbound',
732 tmp_file.write(tunnel)
734 vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
735 copy_on_execute=True)
736 os.remove(tmp_filename)
739 raddr_ip = ip_address(unicode(raddr_ip))
740 laddr_range = '::/0' if raddr_ip.version == 6 else '0.0.0.0/0'
742 cmd = 'ipsec_spd_entry_add_del'
743 err_msg = 'Failed to add entry to Security Policy Database ' \
744 '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
748 priority=int(priority),
749 is_outbound=0 if inbound else 1,
750 sa_id=int(sa_id) if sa_id else 0,
751 policy=IPsecUtil.policy_action_protect().policy_int_repr,
753 remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
754 remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
755 local_address_start=IPUtil.create_ip_address_object(
756 ip_network(unicode(laddr_range), strict=False).network_address),
757 local_address_stop=IPUtil.create_ip_address_object(
759 unicode(laddr_range), strict=False).broadcast_address),
761 remote_port_stop=65535,
763 local_port_stop=65535
770 with PapiSocketExecutor(node) as papi_exec:
771 for i in xrange(n_entries):
772 args['entry']['remote_address_start']['un'] = \
773 IPUtil.union_addr(raddr_ip + i)
774 args['entry']['remote_address_stop']['un'] = \
775 IPUtil.union_addr(raddr_ip + i)
776 history = False if 1 < i < n_entries - 1 else True
777 papi_exec.add(cmd, history=history, **args)
778 if i > 0 and i % Constants.PAPI_MAX_API_BULK == 0:
779 papi_exec.get_replies(err_msg)
780 papi_exec.get_replies(err_msg)
783 def vpp_ipsec_create_tunnel_interfaces(
784 nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
785 crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range):
786 """Create multiple IPsec tunnel interfaces between two VPP nodes.
788 :param nodes: VPP nodes to create tunnel interfaces.
789 :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
790 :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
791 :param if1_key: VPP node 1 interface key from topology file.
792 :param if2_key: VPP node 2 interface key from topology file.
793 :param n_tunnels: Number of tunnell interfaces to create.
794 :param crypto_alg: The encryption algorithm name.
795 :param integ_alg: The integrity algorithm name.
796 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
797 first tunnel in direction node1->node2.
798 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
799 first tunnel in direction node2->node1.
800 :param raddr_range: Mask specifying range of Policy selector Remote
801 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
802 and to 128 in case of IPv6.
804 :type if1_ip_addr: str
805 :type if2_ip_addr: str
809 :type crypto_alg: CryptoAlg
810 :type integ_alg: IntegAlg
811 :type raddr_ip1: string
812 :type raddr_ip2: string
813 :type raddr_range: int
815 n_tunnels = int(n_tunnels)
818 if1_ip = ip_address(unicode(if1_ip_addr))
819 if2_ip = ip_address(unicode(if2_ip_addr))
820 raddr_ip1 = ip_address(unicode(raddr_ip1))
821 raddr_ip2 = ip_address(unicode(raddr_ip2))
822 addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
823 else 1 << (32 - raddr_range)
826 tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
827 tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
829 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
831 'exec create loopback interface\n'
832 'exec set interface state loop0 up\n'
833 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
836 uifc=Topology.get_interface_name(
837 nodes['DUT1'], if1_key),
838 mask=96 if if2_ip.version == 6 else 24))
840 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
843 uifc=Topology.get_interface_name(
844 nodes['DUT2'], if2_key),
845 mask=96 if if2_ip.version == 6 else 24))
846 for i in xrange(n_tunnels):
847 ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(
848 crypto_alg)).encode('hex')
850 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(
851 integ_alg)).encode('hex')
853 'integ_alg {integ_alg} '
854 'local_integ_key {local_integ_key} '
855 'remote_integ_key {remote_integ_key} '
857 integ_alg=integ_alg.alg_name,
858 local_integ_key=ikey,
859 remote_integ_key=ikey))
863 'exec set interface ip address loop0 {laddr}/32\n'
864 'ipsec_tunnel_if_add_del '
865 'local_spi {local_spi} '
866 'remote_spi {remote_spi} '
867 'crypto_alg {crypto_alg} '
868 'local_crypto_key {local_crypto_key} '
869 'remote_crypto_key {remote_crypto_key} '
872 'remote_ip {raddr}\n'
875 remote_spi=spi_2 + i,
876 crypto_alg=crypto_alg.alg_name,
877 local_crypto_key=ckey,
878 remote_crypto_key=ckey,
880 laddr=if1_ip + i * addr_incr,
883 'ipsec_tunnel_if_add_del '
884 'local_spi {local_spi} '
885 'remote_spi {remote_spi} '
886 'crypto_alg {crypto_alg} '
887 'local_crypto_key {local_crypto_key} '
888 'remote_crypto_key {remote_crypto_key} '
891 'remote_ip {raddr}\n'
894 remote_spi=spi_1 + i,
895 crypto_alg=crypto_alg.alg_name,
896 local_crypto_key=ckey,
897 remote_crypto_key=ckey,
900 raddr=if1_ip + i * addr_incr))
902 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
903 copy_on_execute=True,
904 history=False if n_tunnels > 100 else True)
906 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
907 copy_on_execute=True,
908 history=False if n_tunnels > 100 else True)
912 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
914 'exec ip route add {raddr} via {uifc} {iaddr}\n'
916 raddr=ip_network(unicode(if1_ip_addr+'/8'), False),
918 uifc=Topology.get_interface_name(
919 nodes['DUT2'], if2_key)))
920 for i in xrange(n_tunnels):
922 'exec set interface unnumbered ipsec{i} use {uifc}\n'
923 'exec set interface state ipsec{i} up\n'
924 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
928 uifc=Topology.get_interface_name(nodes['DUT1'],
930 mask=128 if if2_ip.version == 6 else 32))
932 'exec set interface unnumbered ipsec{i} use {uifc}\n'
933 'exec set interface state ipsec{i} up\n'
934 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
938 uifc=Topology.get_interface_name(nodes['DUT2'],
940 mask=128 if if2_ip.version == 6 else 32))
942 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
943 copy_on_execute=True,
944 history=False if n_tunnels > 100 else True)
946 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
947 copy_on_execute=True,
948 history=False if n_tunnels > 100 else True)
953 with PapiSocketExecutor(nodes['DUT1']) as papi_exec:
954 # Create loopback interface on DUT1, set it to up state
955 cmd1 = 'create_loopback'
956 args1 = dict(mac_address=0)
957 err_msg = 'Failed to create loopback interface on host {host}'.\
958 format(host=nodes['DUT1']['host'])
959 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
960 get_sw_if_index(err_msg)
961 cmd1 = 'sw_interface_set_flags'
963 sw_if_index=loop_sw_if_idx,
965 err_msg = 'Failed to set loopback interface state up on host ' \
966 '{host}'.format(host=nodes['DUT1']['host'])
967 papi_exec.add(cmd1, **args1).get_reply(err_msg)
968 # Set IP address on VPP node 1 interface
969 cmd1 = 'sw_interface_add_del_address'
971 sw_if_index=InterfaceUtil.get_interface_index(
972 nodes['DUT1'], if1_key),
974 is_ipv6=1 if if2_ip.version == 6 else 0,
976 address_length=96 if if2_ip.version == 6 else 24,
977 address=getattr(if2_ip - 1, 'packed'))
978 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
979 '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host'])
980 papi_exec.add(cmd1, **args1).get_reply(err_msg)
981 # Configure IPsec tunnel interfaces
983 sw_if_index=loop_sw_if_idx,
985 is_ipv6=1 if if1_ip.version == 6 else 0,
987 address_length=128 if if1_ip.version == 6 else 32,
989 cmd2 = 'ipsec_tunnel_if_add_del'
996 crypto_alg=crypto_alg.alg_int_repr,
997 local_crypto_key_len=0,
998 local_crypto_key=None,
999 remote_crypto_key_len=0,
1000 remote_crypto_key=None,
1001 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1002 local_integ_key_len=0,
1003 local_integ_key=None,
1004 remote_integ_key_len=0,
1005 remote_integ_key=None,
1008 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
1009 format(host=nodes['DUT1']['host'])
1010 ipsec_tunnels = list()
1013 for i in xrange(n_tunnels):
1015 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1018 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1019 args1['address'] = getattr(if1_ip + i * addr_incr, 'packed')
1020 args2['local_spi'] = spi_1 + i
1021 args2['remote_spi'] = spi_2 + i
1022 args2['local_ip'] = IPUtil.create_ip_address_object(
1023 if1_ip + i * addr_incr)
1024 args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip)
1025 args2['local_crypto_key_len'] = len(ckeys[i])
1026 args2['local_crypto_key'] = ckeys[i]
1027 args2['remote_crypto_key_len'] = len(ckeys[i])
1028 args2['remote_crypto_key'] = ckeys[i]
1030 args2['local_integ_key_len'] = len(ikeys[i])
1031 args2['local_integ_key'] = ikeys[i]
1032 args2['remote_integ_key_len'] = len(ikeys[i])
1033 args2['remote_integ_key'] = ikeys[i]
1034 history = False if 1 < i < n_tunnels - 1 else True
1035 papi_exec.add(cmd1, history=history, **args1).\
1036 add(cmd2, history=history, **args2)
1037 if i > 0 and i % Constants.PAPI_MAX_API_BULK / 2 == 0:
1038 replies = papi_exec.get_replies(err_msg)
1039 for reply in replies:
1040 if 'sw_if_index' in reply:
1041 ipsec_tunnels.append(reply["sw_if_index"])
1042 replies = papi_exec.get_replies(err_msg)
1043 for reply in replies:
1044 if 'sw_if_index' in reply:
1045 ipsec_tunnels.append(reply["sw_if_index"])
1046 # Configure IP routes
1047 cmd1 = 'sw_interface_set_unnumbered'
1050 sw_if_index=InterfaceUtil.get_interface_index(
1051 nodes['DUT1'], if1_key),
1052 unnumbered_sw_if_index=0
1054 cmd2 = 'sw_interface_set_flags'
1058 cmd3 = 'ip_route_add_del'
1059 route = IPUtil.compose_vpp_route_structure(
1060 nodes['DUT1'], raddr_ip2.compressed,
1061 prefix_len=128 if raddr_ip2.version == 6 else 32,
1069 err_msg = 'Failed to add IP routes on host {host}'.format(
1070 host=nodes['DUT1']['host'])
1071 for i in xrange(n_tunnels):
1072 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1073 args2['sw_if_index'] = ipsec_tunnels[i]
1074 args3['route']['prefix']['address']['un'] = \
1075 IPUtil.union_addr(raddr_ip2 + i)
1076 args3['route']['paths'][0]['sw_if_index'] = \
1078 history = False if 1 < i < n_tunnels - 1 else True
1079 papi_exec.add(cmd1, history=history, **args1).\
1080 add(cmd2, history=history, **args2).\
1081 add(cmd3, history=history, **args3)
1082 if i > 0 and i % Constants.PAPI_MAX_API_BULK / 3 == 0:
1083 papi_exec.get_replies(err_msg)
1084 papi_exec.get_replies(err_msg)
1086 with PapiSocketExecutor(nodes['DUT2']) as papi_exec:
1087 # Set IP address on VPP node 2 interface
1088 cmd1 = 'sw_interface_add_del_address'
1090 sw_if_index=InterfaceUtil.get_interface_index(
1091 nodes['DUT2'], if2_key),
1093 is_ipv6=1 if if2_ip.version == 6 else 0,
1095 address_length=96 if if2_ip.version == 6 else 24,
1096 address=if2_ip.packed)
1097 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
1098 '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host'])
1099 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1100 # Configure IPsec tunnel interfaces
1101 cmd2 = 'ipsec_tunnel_if_add_del'
1104 local_ip=IPUtil.create_ip_address_object(if2_ip),
1108 crypto_alg=crypto_alg.alg_int_repr,
1109 local_crypto_key_len=0,
1110 local_crypto_key=None,
1111 remote_crypto_key_len=0,
1112 remote_crypto_key=None,
1113 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1114 local_integ_key_len=0,
1115 local_integ_key=None,
1116 remote_integ_key_len=0,
1117 remote_integ_key=None,
1120 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \
1121 format(host=nodes['DUT2']['host'])
1122 ipsec_tunnels = list()
1123 for i in xrange(n_tunnels):
1124 args2['local_spi'] = spi_2 + i
1125 args2['remote_spi'] = spi_1 + i
1126 args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip)
1127 args2['remote_ip'] = IPUtil.create_ip_address_object(
1128 if1_ip + i * addr_incr)
1129 args2['local_crypto_key_len'] = len(ckeys[i])
1130 args2['local_crypto_key'] = ckeys[i]
1131 args2['remote_crypto_key_len'] = len(ckeys[i])
1132 args2['remote_crypto_key'] = ckeys[i]
1134 args2['local_integ_key_len'] = len(ikeys[i])
1135 args2['local_integ_key'] = ikeys[i]
1136 args2['remote_integ_key_len'] = len(ikeys[i])
1137 args2['remote_integ_key'] = ikeys[i]
1138 history = False if 1 < i < n_tunnels - 1 else True
1139 papi_exec.add(cmd2, history=history, **args2)
1140 if i > 0 and i % Constants.PAPI_MAX_API_BULK / 2 == 0:
1141 replies = papi_exec.get_replies(err_msg)
1142 for reply in replies:
1143 if 'sw_if_index' in reply:
1144 ipsec_tunnels.append(reply["sw_if_index"])
1145 replies = papi_exec.get_replies(err_msg)
1146 for reply in replies:
1147 if 'sw_if_index' in reply:
1148 ipsec_tunnels.append(reply["sw_if_index"])
1149 # Configure IP routes
1150 cmd1 = 'ip_route_add_del'
1151 route = IPUtil.compose_vpp_route_structure(
1152 nodes['DUT2'], if1_ip.compressed,
1153 prefix_len=32 if if1_ip.version == 6 else 8,
1155 gateway=(if2_ip - 1).compressed
1162 papi_exec.add(cmd1, **args1)
1163 cmd1 = 'sw_interface_set_unnumbered'
1166 sw_if_index=InterfaceUtil.get_interface_index(
1167 nodes['DUT2'], if2_key),
1168 unnumbered_sw_if_index=0
1170 cmd2 = 'sw_interface_set_flags'
1174 cmd3 = 'ip_route_add_del'
1175 route = IPUtil.compose_vpp_route_structure(
1176 nodes['DUT2'], raddr_ip1.compressed,
1177 prefix_len=128 if raddr_ip1.version == 6 else 32,
1185 err_msg = 'Failed to add IP routes on host {host}'.format(
1186 host=nodes['DUT2']['host'])
1187 for i in xrange(n_tunnels):
1188 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1189 args2['sw_if_index'] = ipsec_tunnels[i]
1190 args3['route']['prefix']['address']['un'] = \
1191 IPUtil.union_addr(raddr_ip1 + i)
1192 args3['route']['paths'][0]['sw_if_index'] = \
1194 history = False if 1 < i < n_tunnels - 1 else True
1195 papi_exec.add(cmd1, history=history, **args1). \
1196 add(cmd2, history=history, **args2). \
1197 add(cmd3, history=history, **args3)
1198 if i > 0 and i % Constants.PAPI_MAX_API_BULK / 3 == 0:
1199 papi_exec.get_replies(err_msg)
1200 papi_exec.get_replies(err_msg)
1203 def vpp_ipsec_add_multiple_tunnels(
1204 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1205 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1206 """Create multiple IPsec tunnels between two VPP nodes.
1208 :param nodes: VPP nodes to create tunnels.
1209 :param interface1: Interface name or sw_if_index on node 1.
1210 :param interface2: Interface name or sw_if_index on node 2.
1211 :param n_tunnels: Number of tunnels to create.
1212 :param crypto_alg: The encryption algorithm name.
1213 :param integ_alg: The integrity algorithm name.
1214 :param tunnel_ip1: Tunnel node1 IPv4 address.
1215 :param tunnel_ip2: Tunnel node2 IPv4 address.
1216 :param raddr_ip1: Policy selector remote IPv4 start address for the
1217 first tunnel in direction node1->node2.
1218 :param raddr_ip2: Policy selector remote IPv4 start address for the
1219 first tunnel in direction node2->node1.
1220 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1221 addresses. Valid values are from 1 to 32.
1223 :type interface1: str or int
1224 :type interface2: str or int
1225 :type n_tunnels: int
1226 :type crypto_alg: CryptoAlg
1227 :type integ_alg: IntegAlg
1228 :type tunnel_ip1: str
1229 :type tunnel_ip2: str
1230 :type raddr_ip1: string
1231 :type raddr_ip2: string
1232 :type raddr_range: int
1242 crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1243 integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \
1244 if integ_alg else ''
1246 IPsecUtil.vpp_ipsec_set_ip_route(
1247 nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1248 interface1, raddr_range)
1249 IPsecUtil.vpp_ipsec_set_ip_route(
1250 nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1251 interface2, raddr_range)
1253 IPsecUtil.vpp_ipsec_add_spd(
1254 nodes['DUT1'], spd_id)
1255 IPsecUtil.vpp_ipsec_spd_add_if(
1256 nodes['DUT1'], spd_id, interface1)
1257 IPsecUtil.vpp_ipsec_policy_add(
1258 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1259 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1260 IPsecUtil.vpp_ipsec_policy_add(
1261 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1262 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1264 IPsecUtil.vpp_ipsec_add_spd(
1265 nodes['DUT2'], spd_id)
1266 IPsecUtil.vpp_ipsec_spd_add_if(
1267 nodes['DUT2'], spd_id, interface2)
1268 IPsecUtil.vpp_ipsec_policy_add(
1269 nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1270 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1271 IPsecUtil.vpp_ipsec_policy_add(
1272 nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1273 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1275 IPsecUtil.vpp_ipsec_add_sad_entries(
1276 nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1277 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1279 IPsecUtil.vpp_ipsec_spd_add_entries(
1280 nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
1282 IPsecUtil.vpp_ipsec_add_sad_entries(
1283 nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1284 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1286 IPsecUtil.vpp_ipsec_spd_add_entries(
1287 nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
1289 IPsecUtil.vpp_ipsec_add_sad_entries(
1290 nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1291 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1293 IPsecUtil.vpp_ipsec_spd_add_entries(
1294 nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
1296 IPsecUtil.vpp_ipsec_add_sad_entries(
1297 nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1298 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1300 IPsecUtil.vpp_ipsec_spd_add_entries(
1301 nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
1304 def vpp_ipsec_show(node):
1305 """Run "show ipsec" debug CLI command.
1307 :param node: Node to run command on.
1310 PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')