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.IPUtil import IPUtil
25 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 papi_exec.get_replies(err_msg)
467 def vpp_ipsec_set_ip_route(
468 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
470 """Set IP address and route on interface.
472 :param node: VPP node to add config on.
473 :param n_tunnels: Number of tunnels to create.
474 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
475 :param traffic_addr: Traffic destination IP address to route.
476 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
477 :param interface: Interface key on node 1.
478 :param raddr_range: Mask specifying range of Policy selector Remote IP
479 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
483 :type tunnel_src: str
484 :type traffic_addr: str
485 :type tunnel_dst: str
487 :type raddr_range: int
489 laddr = ip_address(unicode(tunnel_src))
490 raddr = ip_address(unicode(tunnel_dst))
491 taddr = ip_address(unicode(traffic_addr))
492 addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
493 else 1 << (32 - raddr_range)
495 if int(n_tunnels) > 10:
496 tmp_filename = '/tmp/ipsec_set_ip.script'
498 with open(tmp_filename, 'w') as tmp_file:
499 for i in xrange(n_tunnels):
501 'exec set interface ip address {interface} '
502 '{laddr}/{laddr_l}\n'
503 'exec ip route add {taddr}/{taddr_l} via {raddr} '
504 '{interface}\n'.format(
505 interface=Topology.get_interface_name(
507 laddr=laddr + i * addr_incr,
509 raddr=raddr + i * addr_incr,
511 taddr_l=128 if taddr.version == 6 else 32))
514 vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
515 copy_on_execute=True)
516 os.remove(tmp_filename)
519 cmd1 = 'sw_interface_add_del_address'
521 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
526 cmd2 = 'ip_route_add_del'
532 err_msg = 'Failed to configure IP addresses and IP routes on ' \
533 'interface {ifc} on host {host}'.\
534 format(ifc=interface, host=node['host'])
536 with PapiSocketExecutor(node) as papi_exec:
537 for i in xrange(n_tunnels):
538 args1['prefix'] = IPUtil.create_prefix_object(
539 laddr + i * addr_incr, raddr_range)
540 args2['route'] = IPUtil.compose_vpp_route_structure(
543 prefix_len=128 if taddr.version == 6 else 32,
545 gateway=raddr + i * addr_incr
547 history = False if 1 < i < n_tunnels - 1 else True
548 papi_exec.add(cmd1, history=history, **args1).\
549 add(cmd2, history=history, **args2)
550 papi_exec.get_replies(err_msg)
553 def vpp_ipsec_add_spd(node, spd_id):
554 """Create Security Policy Database on the VPP node.
556 :param node: VPP node to add SPD on.
557 :param spd_id: SPD ID.
561 cmd = 'ipsec_spd_add_del'
562 err_msg = 'Failed to add Security Policy Database on host {host}'.\
563 format(host=node['host'])
568 with PapiSocketExecutor(node) as papi_exec:
569 papi_exec.add(cmd, **args).get_reply(err_msg)
572 def vpp_ipsec_spd_add_if(node, spd_id, interface):
573 """Add interface to the Security Policy Database.
575 :param node: VPP node.
576 :param spd_id: SPD ID to add interface on.
577 :param interface: Interface name or sw_if_index.
580 :type interface: str or int
582 cmd = 'ipsec_interface_add_del_spd'
583 err_msg = 'Failed to add interface {ifc} to Security Policy Database ' \
584 '{spd} on host {host}'.\
585 format(ifc=interface, spd=spd_id, host=node['host'])
588 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
591 with PapiSocketExecutor(node) as papi_exec:
592 papi_exec.add(cmd, **args).get_reply(err_msg)
595 def vpp_ipsec_policy_add(
596 node, spd_id, priority, action, inbound=True, sa_id=None,
597 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
598 rport_range=None, is_ipv6=False):
599 """Create Security Policy Database entry on the VPP node.
601 :param node: VPP node to add SPD entry on.
602 :param spd_id: SPD ID to add entry on.
603 :param priority: SPD entry priority, higher number = higher priority.
604 :param action: Policy action.
605 :param inbound: If True policy is for inbound traffic, otherwise
607 :param sa_id: SAD entry ID for protect action.
608 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
609 format IP/prefix or IP/mask. If no mask is provided,
610 it's considered to be /32.
611 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
612 format IP/prefix or IP/mask. If no mask is provided,
613 it's considered to be /32.
614 :param proto: Policy selector next layer protocol number.
615 :param lport_range: Policy selector local TCP/UDP port range in format
616 <port_start>-<port_end>.
617 :param rport_range: Policy selector remote TCP/UDP port range in format
618 <port_start>-<port_end>.
619 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
620 not defined so it will default to address ::/0, otherwise False.
624 :type action: PolicyAction
627 :type laddr_range: string
628 :type raddr_range: string
630 :type lport_range: string
631 :type rport_range: string
635 if laddr_range is None:
636 laddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
638 if raddr_range is None:
639 raddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
641 cmd = 'ipsec_spd_entry_add_del'
642 err_msg = 'Failed to add entry to Security Policy Database ' \
643 '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
647 priority=int(priority),
648 is_outbound=0 if inbound else 1,
649 sa_id=int(sa_id) if sa_id else 0,
650 policy=action.policy_int_repr,
651 protocol=int(proto) if proto else 0,
652 remote_address_start=IPUtil.create_ip_address_object(
653 ip_network(unicode(raddr_range), strict=False).network_address),
654 remote_address_stop=IPUtil.create_ip_address_object(
656 unicode(raddr_range), strict=False).broadcast_address),
657 local_address_start=IPUtil.create_ip_address_object(
659 unicode(laddr_range), strict=False).network_address),
660 local_address_stop=IPUtil.create_ip_address_object(
662 unicode(laddr_range), strict=False).broadcast_address),
663 remote_port_start=int(rport_range.split('-')[0]) if rport_range
665 remote_port_stop=int(rport_range.split('-')[1]) if rport_range
667 local_port_start=int(lport_range.split('-')[0]) if lport_range
669 local_port_stop=int(lport_range.split('-')[1]) if rport_range
676 with PapiSocketExecutor(node) as papi_exec:
677 papi_exec.add(cmd, **args).get_reply(err_msg)
680 def vpp_ipsec_spd_add_entries(
681 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip):
682 """Create multiple Security Policy Database entries on the VPP node.
684 :param node: VPP node to add SPD entries on.
685 :param n_entries: Number of SPD entries to be added.
686 :param spd_id: SPD ID to add entries on.
687 :param priority: SPD entries priority, higher number = higher priority.
688 :param inbound: If True policy is for inbound traffic, otherwise
690 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
691 SAD entry ID incremented by 1.
692 :param raddr_ip: Policy selector remote IPv4 start address for the first
693 entry. Remote IPv4 end address will be calculated depending on
694 raddr_range parameter. Each subsequent entry will have start address
695 next after IPv4 end address of previous entry.
702 :type raddr_ip: string
704 if int(n_entries) > 10:
705 tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.\
708 with open(tmp_filename, 'w') as tmp_file:
709 for i in xrange(n_entries):
710 raddr_s = ip_address(unicode(raddr_ip)) + i
711 raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1
713 'exec ipsec policy add spd {spd_id} '
714 'priority {priority} {direction} action protect '
715 'sa {sa_id} remote-ip-range {raddr_s} - {raddr_e} '
716 'local-ip-range 0.0.0.0 - 255.255.255.255\n'.
720 direction='inbound' if inbound else 'outbound',
724 tmp_file.write(tunnel)
725 VatExecutor().execute_script(
726 tmp_filename, node, timeout=300, json_out=False,
727 copy_on_execute=True)
728 os.remove(tmp_filename)
731 raddr_ip = ip_address(unicode(raddr_ip))
732 laddr_range = '::/0' if raddr_ip.version == 6 else '0.0.0.0/0'
734 cmd = 'ipsec_spd_entry_add_del'
735 err_msg = 'Failed to add entry to Security Policy Database ' \
736 '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
740 priority=int(priority),
741 is_outbound=0 if inbound else 1,
742 sa_id=int(sa_id) if sa_id else 0,
743 policy=IPsecUtil.policy_action_protect().policy_int_repr,
745 remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
746 remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
747 local_address_start=IPUtil.create_ip_address_object(
748 ip_network(unicode(laddr_range), strict=False).network_address),
749 local_address_stop=IPUtil.create_ip_address_object(
751 unicode(laddr_range), strict=False).broadcast_address),
753 remote_port_stop=65535,
755 local_port_stop=65535
762 with PapiSocketExecutor(node) as papi_exec:
763 for i in xrange(n_entries):
764 args['entry']['remote_address_start']['un'] = \
765 IPUtil.union_addr(raddr_ip + i)
766 args['entry']['remote_address_stop']['un'] = \
767 IPUtil.union_addr(raddr_ip + i)
768 history = False if 1 < i < n_entries - 1 else True
769 papi_exec.add(cmd, history=history, **args)
770 papi_exec.get_replies(err_msg)
773 def vpp_ipsec_create_tunnel_interfaces(
774 nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
775 crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range):
776 """Create multiple IPsec tunnel interfaces between two VPP nodes.
778 :param nodes: VPP nodes to create tunnel interfaces.
779 :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
780 :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
781 :param if1_key: VPP node 1 interface key from topology file.
782 :param if2_key: VPP node 2 interface key from topology file.
783 :param n_tunnels: Number of tunnel interfaces to create.
784 :param crypto_alg: The encryption algorithm name.
785 :param integ_alg: The integrity algorithm name.
786 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
787 first tunnel in direction node1->node2.
788 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
789 first tunnel in direction node2->node1.
790 :param raddr_range: Mask specifying range of Policy selector Remote
791 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
792 and to 128 in case of IPv6.
794 :type if1_ip_addr: str
795 :type if2_ip_addr: str
799 :type crypto_alg: CryptoAlg
800 :type integ_alg: IntegAlg
801 :type raddr_ip1: string
802 :type raddr_ip2: string
803 :type raddr_range: int
805 n_tunnels = int(n_tunnels)
808 if1_ip = ip_address(unicode(if1_ip_addr))
809 if2_ip = ip_address(unicode(if2_ip_addr))
810 raddr_ip1 = ip_address(unicode(raddr_ip1))
811 raddr_ip2 = ip_address(unicode(raddr_ip2))
812 addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
813 else 1 << (32 - raddr_range)
816 tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
817 tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
819 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
821 'exec create loopback interface\n'
822 'exec set interface state loop0 up\n'
823 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
826 uifc=Topology.get_interface_name(
827 nodes['DUT1'], if1_key),
828 mask=96 if if2_ip.version == 6 else 24))
830 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
833 uifc=Topology.get_interface_name(
834 nodes['DUT2'], if2_key),
835 mask=96 if if2_ip.version == 6 else 24))
836 for i in xrange(n_tunnels):
837 ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(
838 crypto_alg)).encode('hex')
840 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(
841 integ_alg)).encode('hex')
843 'integ_alg {integ_alg} '
844 'local_integ_key {local_integ_key} '
845 'remote_integ_key {remote_integ_key} '
847 integ_alg=integ_alg.alg_name,
848 local_integ_key=ikey,
849 remote_integ_key=ikey))
853 'exec set interface ip address loop0 {laddr}/32\n'
854 'ipsec_tunnel_if_add_del '
855 'local_spi {local_spi} '
856 'remote_spi {remote_spi} '
857 'crypto_alg {crypto_alg} '
858 'local_crypto_key {local_crypto_key} '
859 'remote_crypto_key {remote_crypto_key} '
862 'remote_ip {raddr}\n'
865 remote_spi=spi_2 + i,
866 crypto_alg=crypto_alg.alg_name,
867 local_crypto_key=ckey,
868 remote_crypto_key=ckey,
870 laddr=if1_ip + i * addr_incr,
873 'ipsec_tunnel_if_add_del '
874 'local_spi {local_spi} '
875 'remote_spi {remote_spi} '
876 'crypto_alg {crypto_alg} '
877 'local_crypto_key {local_crypto_key} '
878 'remote_crypto_key {remote_crypto_key} '
881 'remote_ip {raddr}\n'
884 remote_spi=spi_1 + i,
885 crypto_alg=crypto_alg.alg_name,
886 local_crypto_key=ckey,
887 remote_crypto_key=ckey,
890 raddr=if1_ip + i * addr_incr))
892 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
893 copy_on_execute=True,
894 history=False if n_tunnels > 100 else True)
896 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
897 copy_on_execute=True,
898 history=False if n_tunnels > 100 else True)
902 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
904 'exec ip route add {raddr} via {uifc} {iaddr}\n'
906 raddr=ip_network(unicode(if1_ip_addr+'/8'), False),
908 uifc=Topology.get_interface_name(
909 nodes['DUT2'], if2_key)))
910 for i in xrange(n_tunnels):
912 'exec set interface unnumbered ipsec{i} use {uifc}\n'
913 'exec set interface state ipsec{i} up\n'
914 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
918 uifc=Topology.get_interface_name(nodes['DUT1'],
920 mask=128 if if2_ip.version == 6 else 32))
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['DUT2'],
930 mask=128 if if2_ip.version == 6 else 32))
932 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
933 copy_on_execute=True,
934 history=False if n_tunnels > 100 else True)
936 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
937 copy_on_execute=True,
938 history=False if n_tunnels > 100 else True)
943 with PapiSocketExecutor(nodes['DUT1']) as papi_exec:
944 # Create loopback interface on DUT1, set it to up state
945 cmd1 = 'create_loopback'
946 args1 = dict(mac_address=0)
947 err_msg = 'Failed to create loopback interface on host {host}'.\
948 format(host=nodes['DUT1']['host'])
949 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
950 get_sw_if_index(err_msg)
951 cmd1 = 'sw_interface_set_flags'
953 sw_if_index=loop_sw_if_idx,
954 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
955 err_msg = 'Failed to set loopback interface state up on host ' \
956 '{host}'.format(host=nodes['DUT1']['host'])
957 papi_exec.add(cmd1, **args1).get_reply(err_msg)
958 # Set IP address on VPP node 1 interface
959 cmd1 = 'sw_interface_add_del_address'
961 sw_if_index=InterfaceUtil.get_interface_index(
962 nodes['DUT1'], if1_key),
965 prefix=IPUtil.create_prefix_object(
966 if2_ip - 1, 96 if if2_ip.version == 6 else 24)
968 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
969 '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host'])
970 papi_exec.add(cmd1, **args1).get_reply(err_msg)
971 # Configure IPsec tunnel interfaces
973 sw_if_index=loop_sw_if_idx,
978 cmd2 = 'ipsec_tunnel_if_add_del'
985 crypto_alg=crypto_alg.alg_int_repr,
986 local_crypto_key_len=0,
987 local_crypto_key=None,
988 remote_crypto_key_len=0,
989 remote_crypto_key=None,
990 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
991 local_integ_key_len=0,
992 local_integ_key=None,
993 remote_integ_key_len=0,
994 remote_integ_key=None,
997 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
998 format(host=nodes['DUT1']['host'])
999 ipsec_tunnels = list()
1002 for i in xrange(n_tunnels):
1004 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1007 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1008 args1['prefix'] = IPUtil.create_prefix_object(
1009 if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32)
1010 args2['local_spi'] = spi_1 + i
1011 args2['remote_spi'] = spi_2 + i
1012 args2['local_ip'] = IPUtil.create_ip_address_object(
1013 if1_ip + i * addr_incr)
1014 args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip)
1015 args2['local_crypto_key_len'] = len(ckeys[i])
1016 args2['local_crypto_key'] = ckeys[i]
1017 args2['remote_crypto_key_len'] = len(ckeys[i])
1018 args2['remote_crypto_key'] = ckeys[i]
1020 args2['local_integ_key_len'] = len(ikeys[i])
1021 args2['local_integ_key'] = ikeys[i]
1022 args2['remote_integ_key_len'] = len(ikeys[i])
1023 args2['remote_integ_key'] = ikeys[i]
1024 history = False if 1 < i < n_tunnels - 1 else True
1025 papi_exec.add(cmd1, history=history, **args1).\
1026 add(cmd2, history=history, **args2)
1027 replies = papi_exec.get_replies(err_msg)
1028 for reply in replies:
1029 if 'sw_if_index' in reply:
1030 ipsec_tunnels.append(reply["sw_if_index"])
1031 # Configure IP routes
1032 cmd1 = 'sw_interface_set_unnumbered'
1035 sw_if_index=InterfaceUtil.get_interface_index(
1036 nodes['DUT1'], if1_key),
1037 unnumbered_sw_if_index=0
1039 cmd2 = 'sw_interface_set_flags'
1042 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
1043 cmd3 = 'ip_route_add_del'
1049 err_msg = 'Failed to add IP routes on host {host}'.format(
1050 host=nodes['DUT1']['host'])
1051 for i in xrange(n_tunnels):
1052 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1053 args2['sw_if_index'] = ipsec_tunnels[i]
1054 args3['route'] = IPUtil.compose_vpp_route_structure(
1056 (raddr_ip2 + i).compressed,
1057 prefix_len=128 if raddr_ip2.version == 6 else 32,
1058 interface=ipsec_tunnels[i]
1060 history = False if 1 < i < n_tunnels - 1 else True
1061 papi_exec.add(cmd1, history=history, **args1).\
1062 add(cmd2, history=history, **args2).\
1063 add(cmd3, history=history, **args3)
1064 papi_exec.get_replies(err_msg)
1066 with PapiSocketExecutor(nodes['DUT2']) as papi_exec:
1067 # Set IP address on VPP node 2 interface
1068 cmd1 = 'sw_interface_add_del_address'
1070 sw_if_index=InterfaceUtil.get_interface_index(
1071 nodes['DUT2'], if2_key),
1074 prefix=IPUtil.create_prefix_object(
1075 if2_ip, 96 if if2_ip.version == 6 else 24)
1077 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
1078 '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host'])
1079 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1080 # Configure IPsec tunnel interfaces
1081 cmd2 = 'ipsec_tunnel_if_add_del'
1084 local_ip=IPUtil.create_ip_address_object(if2_ip),
1088 crypto_alg=crypto_alg.alg_int_repr,
1089 local_crypto_key_len=0,
1090 local_crypto_key=None,
1091 remote_crypto_key_len=0,
1092 remote_crypto_key=None,
1093 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1094 local_integ_key_len=0,
1095 local_integ_key=None,
1096 remote_integ_key_len=0,
1097 remote_integ_key=None,
1100 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \
1101 format(host=nodes['DUT2']['host'])
1102 ipsec_tunnels = list()
1103 for i in xrange(n_tunnels):
1104 args2['local_spi'] = spi_2 + i
1105 args2['remote_spi'] = spi_1 + i
1106 args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip)
1107 args2['remote_ip'] = IPUtil.create_ip_address_object(
1108 if1_ip + i * addr_incr)
1109 args2['local_crypto_key_len'] = len(ckeys[i])
1110 args2['local_crypto_key'] = ckeys[i]
1111 args2['remote_crypto_key_len'] = len(ckeys[i])
1112 args2['remote_crypto_key'] = ckeys[i]
1114 args2['local_integ_key_len'] = len(ikeys[i])
1115 args2['local_integ_key'] = ikeys[i]
1116 args2['remote_integ_key_len'] = len(ikeys[i])
1117 args2['remote_integ_key'] = ikeys[i]
1118 history = False if 1 < i < n_tunnels - 1 else True
1119 papi_exec.add(cmd2, history=history, **args2)
1120 replies = papi_exec.get_replies(err_msg)
1121 for reply in replies:
1122 if 'sw_if_index' in reply:
1123 ipsec_tunnels.append(reply["sw_if_index"])
1124 # Configure IP routes
1125 cmd1 = 'ip_route_add_del'
1126 route = IPUtil.compose_vpp_route_structure(
1127 nodes['DUT2'], if1_ip.compressed,
1128 prefix_len=32 if if1_ip.version == 6 else 8,
1130 gateway=(if2_ip - 1).compressed
1137 papi_exec.add(cmd1, **args1)
1138 cmd1 = 'sw_interface_set_unnumbered'
1141 sw_if_index=InterfaceUtil.get_interface_index(
1142 nodes['DUT2'], if2_key),
1143 unnumbered_sw_if_index=0
1145 cmd2 = 'sw_interface_set_flags'
1148 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
1149 cmd3 = 'ip_route_add_del'
1155 err_msg = 'Failed to add IP routes on host {host}'.format(
1156 host=nodes['DUT2']['host'])
1157 for i in xrange(n_tunnels):
1158 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1159 args2['sw_if_index'] = ipsec_tunnels[i]
1160 args3['route'] = IPUtil.compose_vpp_route_structure(
1162 (raddr_ip1 + i).compressed,
1163 prefix_len=128 if raddr_ip1.version == 6 else 32,
1164 interface=ipsec_tunnels[i]
1166 history = False if 1 < i < n_tunnels - 1 else True
1167 papi_exec.add(cmd1, history=history, **args1). \
1168 add(cmd2, history=history, **args2). \
1169 add(cmd3, history=history, **args3)
1170 papi_exec.get_replies(err_msg)
1173 def vpp_ipsec_add_multiple_tunnels(
1174 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1175 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1176 """Create multiple IPsec tunnels between two VPP nodes.
1178 :param nodes: VPP nodes to create tunnels.
1179 :param interface1: Interface name or sw_if_index on node 1.
1180 :param interface2: Interface name or sw_if_index on node 2.
1181 :param n_tunnels: Number of tunnels to create.
1182 :param crypto_alg: The encryption algorithm name.
1183 :param integ_alg: The integrity algorithm name.
1184 :param tunnel_ip1: Tunnel node1 IPv4 address.
1185 :param tunnel_ip2: Tunnel node2 IPv4 address.
1186 :param raddr_ip1: Policy selector remote IPv4 start address for the
1187 first tunnel in direction node1->node2.
1188 :param raddr_ip2: Policy selector remote IPv4 start address for the
1189 first tunnel in direction node2->node1.
1190 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1191 addresses. Valid values are from 1 to 32.
1193 :type interface1: str or int
1194 :type interface2: str or int
1195 :type n_tunnels: int
1196 :type crypto_alg: CryptoAlg
1197 :type integ_alg: IntegAlg
1198 :type tunnel_ip1: str
1199 :type tunnel_ip2: str
1200 :type raddr_ip1: string
1201 :type raddr_ip2: string
1202 :type raddr_range: int
1212 crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1213 integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \
1214 if integ_alg else ''
1216 IPsecUtil.vpp_ipsec_set_ip_route(
1217 nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1218 interface1, raddr_range)
1219 IPsecUtil.vpp_ipsec_set_ip_route(
1220 nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1221 interface2, raddr_range)
1223 IPsecUtil.vpp_ipsec_add_spd(
1224 nodes['DUT1'], spd_id)
1225 IPsecUtil.vpp_ipsec_spd_add_if(
1226 nodes['DUT1'], spd_id, interface1)
1227 IPsecUtil.vpp_ipsec_policy_add(
1228 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1229 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1230 IPsecUtil.vpp_ipsec_policy_add(
1231 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1232 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1234 IPsecUtil.vpp_ipsec_add_spd(
1235 nodes['DUT2'], spd_id)
1236 IPsecUtil.vpp_ipsec_spd_add_if(
1237 nodes['DUT2'], spd_id, interface2)
1238 IPsecUtil.vpp_ipsec_policy_add(
1239 nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1240 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1241 IPsecUtil.vpp_ipsec_policy_add(
1242 nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1243 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1245 IPsecUtil.vpp_ipsec_add_sad_entries(
1246 nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1247 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1249 IPsecUtil.vpp_ipsec_spd_add_entries(
1250 nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
1252 IPsecUtil.vpp_ipsec_add_sad_entries(
1253 nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1254 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1256 IPsecUtil.vpp_ipsec_spd_add_entries(
1257 nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
1259 IPsecUtil.vpp_ipsec_add_sad_entries(
1260 nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1261 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1263 IPsecUtil.vpp_ipsec_spd_add_entries(
1264 nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
1266 IPsecUtil.vpp_ipsec_add_sad_entries(
1267 nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1268 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1270 IPsecUtil.vpp_ipsec_spd_add_entries(
1271 nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
1274 def vpp_ipsec_show(node):
1275 """Run "show ipsec" debug CLI command.
1277 :param node: Node to run command on.
1280 PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')