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'
824 'exec set ip arp {uifc} {raddr}/32 {rmac} static\n'
828 uifc=Topology.get_interface_name(
829 nodes['DUT1'], if1_key),
830 rmac=Topology.get_interface_mac(
831 nodes['DUT2'], if2_key),
832 mask=96 if if2_ip.version == 6 else 24))
834 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
837 uifc=Topology.get_interface_name(
838 nodes['DUT2'], if2_key),
839 mask=96 if if2_ip.version == 6 else 24))
840 for i in xrange(n_tunnels):
841 ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(
842 crypto_alg)).encode('hex')
844 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(
845 integ_alg)).encode('hex')
847 'integ_alg {integ_alg} '
848 'local_integ_key {local_integ_key} '
849 'remote_integ_key {remote_integ_key} '
851 integ_alg=integ_alg.alg_name,
852 local_integ_key=ikey,
853 remote_integ_key=ikey))
857 'exec set interface ip address loop0 {laddr}/32\n'
858 'ipsec_tunnel_if_add_del '
859 'local_spi {local_spi} '
860 'remote_spi {remote_spi} '
861 'crypto_alg {crypto_alg} '
862 'local_crypto_key {local_crypto_key} '
863 'remote_crypto_key {remote_crypto_key} '
866 'remote_ip {raddr}\n'
869 remote_spi=spi_2 + i,
870 crypto_alg=crypto_alg.alg_name,
871 local_crypto_key=ckey,
872 remote_crypto_key=ckey,
874 laddr=if1_ip + i * addr_incr,
877 'ipsec_tunnel_if_add_del '
878 'local_spi {local_spi} '
879 'remote_spi {remote_spi} '
880 'crypto_alg {crypto_alg} '
881 'local_crypto_key {local_crypto_key} '
882 'remote_crypto_key {remote_crypto_key} '
885 'remote_ip {raddr}\n'
888 remote_spi=spi_1 + i,
889 crypto_alg=crypto_alg.alg_name,
890 local_crypto_key=ckey,
891 remote_crypto_key=ckey,
894 raddr=if1_ip + i * addr_incr))
896 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
897 copy_on_execute=True,
898 history=False if n_tunnels > 100 else True)
900 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
901 copy_on_execute=True,
902 history=False if n_tunnels > 100 else True)
906 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
908 'exec ip route add {raddr} via {uifc} {iaddr}\n'
910 raddr=ip_network(unicode(if1_ip_addr+'/8'), False),
912 uifc=Topology.get_interface_name(
913 nodes['DUT2'], if2_key)))
914 for i in xrange(n_tunnels):
916 'exec set interface unnumbered ipsec{i} use {uifc}\n'
917 'exec set interface state ipsec{i} up\n'
918 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
922 uifc=Topology.get_interface_name(nodes['DUT1'],
924 mask=128 if if2_ip.version == 6 else 32))
926 'exec set interface unnumbered ipsec{i} use {uifc}\n'
927 'exec set interface state ipsec{i} up\n'
928 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
932 uifc=Topology.get_interface_name(nodes['DUT2'],
934 mask=128 if if2_ip.version == 6 else 32))
936 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
937 copy_on_execute=True,
938 history=False if n_tunnels > 100 else True)
940 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
941 copy_on_execute=True,
942 history=False if n_tunnels > 100 else True)
947 with PapiSocketExecutor(nodes['DUT1']) as papi_exec:
948 # Create loopback interface on DUT1, set it to up state
949 cmd1 = 'create_loopback'
950 args1 = dict(mac_address=0)
951 err_msg = 'Failed to create loopback interface on host {host}'.\
952 format(host=nodes['DUT1']['host'])
953 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
954 get_sw_if_index(err_msg)
955 cmd1 = 'sw_interface_set_flags'
957 sw_if_index=loop_sw_if_idx,
958 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
959 err_msg = 'Failed to set loopback interface state up on host ' \
960 '{host}'.format(host=nodes['DUT1']['host'])
961 papi_exec.add(cmd1, **args1).get_reply(err_msg)
962 # Set IP address on VPP node 1 interface
963 cmd1 = 'sw_interface_add_del_address'
965 sw_if_index=InterfaceUtil.get_interface_index(
966 nodes['DUT1'], if1_key),
969 prefix=IPUtil.create_prefix_object(
970 if2_ip - 1, 96 if if2_ip.version == 6 else 24)
972 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
973 '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host'])
974 papi_exec.add(cmd1, **args1).get_reply(err_msg)
975 cmd4 = 'ip_neighbor_add_del'
979 sw_if_index=Topology.get_interface_sw_index(
980 nodes['DUT1'], if1_key),
983 Topology.get_interface_mac(nodes['DUT2'], if2_key)),
984 ip_address=str(ip_address(unicode(if2_ip_addr)))
987 err_msg = 'Failed to add IP neighbor on interface {ifc}'.format(
989 papi_exec.add(cmd4, **args4).get_reply(err_msg)
990 # Configure IPsec tunnel interfaces
992 sw_if_index=loop_sw_if_idx,
997 cmd2 = 'ipsec_tunnel_if_add_del'
1004 crypto_alg=crypto_alg.alg_int_repr,
1005 local_crypto_key_len=0,
1006 local_crypto_key=None,
1007 remote_crypto_key_len=0,
1008 remote_crypto_key=None,
1009 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1010 local_integ_key_len=0,
1011 local_integ_key=None,
1012 remote_integ_key_len=0,
1013 remote_integ_key=None,
1016 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
1017 format(host=nodes['DUT1']['host'])
1018 ipsec_tunnels = list()
1021 for i in xrange(n_tunnels):
1023 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1026 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1027 args1['prefix'] = IPUtil.create_prefix_object(
1028 if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32)
1029 args2['local_spi'] = spi_1 + i
1030 args2['remote_spi'] = spi_2 + i
1031 args2['local_ip'] = IPUtil.create_ip_address_object(
1032 if1_ip + i * addr_incr)
1033 args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip)
1034 args2['local_crypto_key_len'] = len(ckeys[i])
1035 args2['local_crypto_key'] = ckeys[i]
1036 args2['remote_crypto_key_len'] = len(ckeys[i])
1037 args2['remote_crypto_key'] = ckeys[i]
1039 args2['local_integ_key_len'] = len(ikeys[i])
1040 args2['local_integ_key'] = ikeys[i]
1041 args2['remote_integ_key_len'] = len(ikeys[i])
1042 args2['remote_integ_key'] = ikeys[i]
1043 history = False if 1 < i < n_tunnels - 1 else True
1044 papi_exec.add(cmd1, history=history, **args1).\
1045 add(cmd2, history=history, **args2)
1046 replies = papi_exec.get_replies(err_msg)
1047 for reply in replies:
1048 if 'sw_if_index' in reply:
1049 ipsec_tunnels.append(reply["sw_if_index"])
1050 # Configure IP routes
1051 cmd1 = 'sw_interface_set_unnumbered'
1054 sw_if_index=InterfaceUtil.get_interface_index(
1055 nodes['DUT1'], if1_key),
1056 unnumbered_sw_if_index=0
1058 cmd2 = 'sw_interface_set_flags'
1061 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
1062 cmd3 = 'ip_route_add_del'
1068 err_msg = 'Failed to add IP routes on host {host}'.format(
1069 host=nodes['DUT1']['host'])
1070 for i in xrange(n_tunnels):
1071 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1072 args2['sw_if_index'] = ipsec_tunnels[i]
1073 args3['route'] = IPUtil.compose_vpp_route_structure(
1075 (raddr_ip2 + i).compressed,
1076 prefix_len=128 if raddr_ip2.version == 6 else 32,
1077 interface=ipsec_tunnels[i]
1079 history = False if 1 < i < n_tunnels - 1 else True
1080 papi_exec.add(cmd1, history=history, **args1).\
1081 add(cmd2, history=history, **args2).\
1082 add(cmd3, history=history, **args3)
1083 papi_exec.get_replies(err_msg)
1085 with PapiSocketExecutor(nodes['DUT2']) as papi_exec:
1086 # Set IP address on VPP node 2 interface
1087 cmd1 = 'sw_interface_add_del_address'
1089 sw_if_index=InterfaceUtil.get_interface_index(
1090 nodes['DUT2'], if2_key),
1093 prefix=IPUtil.create_prefix_object(
1094 if2_ip, 96 if if2_ip.version == 6 else 24)
1096 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
1097 '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host'])
1098 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1099 # Configure IPsec tunnel interfaces
1100 cmd2 = 'ipsec_tunnel_if_add_del'
1103 local_ip=IPUtil.create_ip_address_object(if2_ip),
1107 crypto_alg=crypto_alg.alg_int_repr,
1108 local_crypto_key_len=0,
1109 local_crypto_key=None,
1110 remote_crypto_key_len=0,
1111 remote_crypto_key=None,
1112 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1113 local_integ_key_len=0,
1114 local_integ_key=None,
1115 remote_integ_key_len=0,
1116 remote_integ_key=None,
1119 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \
1120 format(host=nodes['DUT2']['host'])
1121 ipsec_tunnels = list()
1122 for i in xrange(n_tunnels):
1123 args2['local_spi'] = spi_2 + i
1124 args2['remote_spi'] = spi_1 + i
1125 args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip)
1126 args2['remote_ip'] = IPUtil.create_ip_address_object(
1127 if1_ip + i * addr_incr)
1128 args2['local_crypto_key_len'] = len(ckeys[i])
1129 args2['local_crypto_key'] = ckeys[i]
1130 args2['remote_crypto_key_len'] = len(ckeys[i])
1131 args2['remote_crypto_key'] = ckeys[i]
1133 args2['local_integ_key_len'] = len(ikeys[i])
1134 args2['local_integ_key'] = ikeys[i]
1135 args2['remote_integ_key_len'] = len(ikeys[i])
1136 args2['remote_integ_key'] = ikeys[i]
1137 history = False if 1 < i < n_tunnels - 1 else True
1138 papi_exec.add(cmd2, history=history, **args2)
1139 replies = papi_exec.get_replies(err_msg)
1140 for reply in replies:
1141 if 'sw_if_index' in reply:
1142 ipsec_tunnels.append(reply["sw_if_index"])
1143 # Configure IP routes
1144 cmd1 = 'ip_route_add_del'
1145 route = IPUtil.compose_vpp_route_structure(
1146 nodes['DUT2'], if1_ip.compressed,
1147 prefix_len=32 if if1_ip.version == 6 else 8,
1149 gateway=(if2_ip - 1).compressed
1156 papi_exec.add(cmd1, **args1)
1157 cmd1 = 'sw_interface_set_unnumbered'
1160 sw_if_index=InterfaceUtil.get_interface_index(
1161 nodes['DUT2'], if2_key),
1162 unnumbered_sw_if_index=0
1164 cmd2 = 'sw_interface_set_flags'
1167 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value)
1168 cmd3 = 'ip_route_add_del'
1174 err_msg = 'Failed to add IP routes on host {host}'.format(
1175 host=nodes['DUT2']['host'])
1176 for i in xrange(n_tunnels):
1177 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1178 args2['sw_if_index'] = ipsec_tunnels[i]
1179 args3['route'] = IPUtil.compose_vpp_route_structure(
1181 (raddr_ip1 + i).compressed,
1182 prefix_len=128 if raddr_ip1.version == 6 else 32,
1183 interface=ipsec_tunnels[i]
1185 history = False if 1 < i < n_tunnels - 1 else True
1186 papi_exec.add(cmd1, history=history, **args1). \
1187 add(cmd2, history=history, **args2). \
1188 add(cmd3, history=history, **args3)
1189 papi_exec.get_replies(err_msg)
1192 def vpp_ipsec_add_multiple_tunnels(
1193 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1194 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1195 """Create multiple IPsec tunnels between two VPP nodes.
1197 :param nodes: VPP nodes to create tunnels.
1198 :param interface1: Interface name or sw_if_index on node 1.
1199 :param interface2: Interface name or sw_if_index on node 2.
1200 :param n_tunnels: Number of tunnels to create.
1201 :param crypto_alg: The encryption algorithm name.
1202 :param integ_alg: The integrity algorithm name.
1203 :param tunnel_ip1: Tunnel node1 IPv4 address.
1204 :param tunnel_ip2: Tunnel node2 IPv4 address.
1205 :param raddr_ip1: Policy selector remote IPv4 start address for the
1206 first tunnel in direction node1->node2.
1207 :param raddr_ip2: Policy selector remote IPv4 start address for the
1208 first tunnel in direction node2->node1.
1209 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1210 addresses. Valid values are from 1 to 32.
1212 :type interface1: str or int
1213 :type interface2: str or int
1214 :type n_tunnels: int
1215 :type crypto_alg: CryptoAlg
1216 :type integ_alg: IntegAlg
1217 :type tunnel_ip1: str
1218 :type tunnel_ip2: str
1219 :type raddr_ip1: string
1220 :type raddr_ip2: string
1221 :type raddr_range: int
1231 crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1232 integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \
1233 if integ_alg else ''
1235 IPsecUtil.vpp_ipsec_set_ip_route(
1236 nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1237 interface1, raddr_range)
1238 IPsecUtil.vpp_ipsec_set_ip_route(
1239 nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1240 interface2, raddr_range)
1242 IPsecUtil.vpp_ipsec_add_spd(
1243 nodes['DUT1'], spd_id)
1244 IPsecUtil.vpp_ipsec_spd_add_if(
1245 nodes['DUT1'], spd_id, interface1)
1246 IPsecUtil.vpp_ipsec_policy_add(
1247 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1248 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1249 IPsecUtil.vpp_ipsec_policy_add(
1250 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1251 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1253 IPsecUtil.vpp_ipsec_add_spd(
1254 nodes['DUT2'], spd_id)
1255 IPsecUtil.vpp_ipsec_spd_add_if(
1256 nodes['DUT2'], spd_id, interface2)
1257 IPsecUtil.vpp_ipsec_policy_add(
1258 nodes['DUT2'], 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['DUT2'], 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_sad_entries(
1265 nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1266 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1268 IPsecUtil.vpp_ipsec_spd_add_entries(
1269 nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
1271 IPsecUtil.vpp_ipsec_add_sad_entries(
1272 nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1273 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1275 IPsecUtil.vpp_ipsec_spd_add_entries(
1276 nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
1278 IPsecUtil.vpp_ipsec_add_sad_entries(
1279 nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1280 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1282 IPsecUtil.vpp_ipsec_spd_add_entries(
1283 nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
1285 IPsecUtil.vpp_ipsec_add_sad_entries(
1286 nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1287 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1289 IPsecUtil.vpp_ipsec_spd_add_entries(
1290 nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
1293 def vpp_ipsec_show(node):
1294 """Run "show ipsec" debug CLI command.
1296 :param node: Node to run command on.
1299 PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')