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
26 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
27 from resources.libraries.python.topology import Topology
28 from resources.libraries.python.VatExecutor import VatExecutor
32 """Generate random string as a key.
34 :param length: Length of generated payload.
36 :returns: The generated payload.
39 return ''.join(choice(letters) for _ in range(length))
42 class PolicyAction(Enum):
44 BYPASS = ('bypass', 0)
45 DISCARD = ('discard', 1)
46 PROTECT = ('protect', 3)
48 def __init__(self, policy_name, policy_int_repr):
49 self.policy_name = policy_name
50 self.policy_int_repr = policy_int_repr
53 class CryptoAlg(Enum):
54 """Encryption algorithms."""
55 AES_CBC_128 = ('aes-cbc-128', 1, 'AES-CBC', 16)
56 AES_CBC_256 = ('aes-cbc-256', 3, 'AES-CBC', 32)
57 AES_GCM_128 = ('aes-gcm-128', 7, 'AES-GCM', 16)
58 AES_GCM_256 = ('aes-gcm-256', 9, 'AES-GCM', 32)
60 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
61 self.alg_name = alg_name
62 self.alg_int_repr = alg_int_repr
63 self.scapy_name = scapy_name
64 self.key_len = key_len
68 """Integrity algorithm."""
69 SHA_256_128 = ('sha-256-128', 4, 'SHA2-256-128', 32)
70 SHA_512_256 = ('sha-512-256', 6, 'SHA2-512-256', 64)
72 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
73 self.alg_name = alg_name
74 self.alg_int_repr = alg_int_repr
75 self.scapy_name = scapy_name
76 self.key_len = key_len
79 class IPsecProto(IntEnum):
85 class IPsecSadFlags(IntEnum):
86 """IPsec Security Association Database flags."""
87 IPSEC_API_SAD_FLAG_NONE = 0
88 IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
89 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
92 class IPsecUtil(object):
93 """IPsec utilities."""
96 def policy_action_bypass():
97 """Return policy action bypass.
99 :returns: PolicyAction enum BYPASS object.
102 return PolicyAction.BYPASS
105 def policy_action_discard():
106 """Return policy action discard.
108 :returns: PolicyAction enum DISCARD object.
111 return PolicyAction.DISCARD
114 def policy_action_protect():
115 """Return policy action protect.
117 :returns: PolicyAction enum PROTECT object.
120 return PolicyAction.PROTECT
123 def crypto_alg_aes_cbc_128():
124 """Return encryption algorithm aes-cbc-128.
126 :returns: CryptoAlg enum AES_CBC_128 object.
129 return CryptoAlg.AES_CBC_128
132 def crypto_alg_aes_cbc_256():
133 """Return encryption algorithm aes-cbc-256.
135 :returns: CryptoAlg enum AES_CBC_256 object.
138 return CryptoAlg.AES_CBC_256
141 def crypto_alg_aes_gcm_128():
142 """Return encryption algorithm aes-gcm-128.
144 :returns: CryptoAlg enum AES_GCM_128 object.
147 return CryptoAlg.AES_GCM_128
150 def crypto_alg_aes_gcm_256():
151 """Return encryption algorithm aes-gcm-256.
153 :returns: CryptoAlg enum AES_GCM_128 object.
156 return CryptoAlg.AES_GCM_256
159 def get_crypto_alg_key_len(crypto_alg):
160 """Return encryption algorithm key length.
162 :param crypto_alg: Encryption algorithm.
163 :type crypto_alg: CryptoAlg
164 :returns: Key length.
167 return crypto_alg.key_len
170 def get_crypto_alg_scapy_name(crypto_alg):
171 """Return encryption algorithm scapy name.
173 :param crypto_alg: Encryption algorithm.
174 :type crypto_alg: CryptoAlg
175 :returns: Algorithm scapy name.
178 return crypto_alg.scapy_name
181 def integ_alg_sha_256_128():
182 """Return integrity algorithm SHA-256-128.
184 :returns: IntegAlg enum SHA_256_128 object.
187 return IntegAlg.SHA_256_128
190 def integ_alg_sha_512_256():
191 """Return integrity algorithm SHA-512-256.
193 :returns: IntegAlg enum SHA_512_256 object.
196 return IntegAlg.SHA_512_256
199 def get_integ_alg_key_len(integ_alg):
200 """Return integrity algorithm key length.
202 :param integ_alg: Integrity algorithm.
203 :type integ_alg: IntegAlg
204 :returns: Key length.
207 return integ_alg.key_len
210 def get_integ_alg_scapy_name(integ_alg):
211 """Return integrity algorithm scapy name.
213 :param integ_alg: Integrity algorithm.
214 :type integ_alg: IntegAlg
215 :returns: Algorithm scapy name.
218 return integ_alg.scapy_name
221 def ipsec_proto_esp():
222 """Return IPSec protocol ESP.
224 :returns: IPsecProto enum ESP object.
227 return int(IPsecProto.ESP)
230 def ipsec_proto_ah():
231 """Return IPSec protocol AH.
233 :returns: IPsecProto enum AH object.
236 return int(IPsecProto.SEC_AH)
239 def vpp_ipsec_select_backend(node, protocol, index=1):
240 """Select IPsec backend.
242 :param node: VPP node to select IPsec backend on.
243 :param protocol: IPsec protocol.
244 :param index: Backend index.
246 :type protocol: IPsecProto
248 :raises RuntimeError: If failed to select IPsec backend or if no API
251 cmd = 'ipsec_select_backend'
252 err_msg = 'Failed to select IPsec backend on host {host}'.format(
258 with PapiSocketExecutor(node) as papi_exec:
259 papi_exec.add(cmd, **args).get_reply(err_msg)
262 def vpp_ipsec_backend_dump(node):
263 """Dump IPsec backends.
265 :param node: VPP node to dump IPsec backend on.
268 err_msg = 'Failed to dump IPsec backends on host {host}'.format(
270 with PapiSocketExecutor(node) as papi_exec:
271 papi_exec.add('ipsec_backend_dump').get_details(err_msg)
274 def vpp_ipsec_add_sad_entry(
275 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
276 integ_key='', tunnel_src=None, tunnel_dst=None):
277 """Create Security Association Database entry on the VPP node.
279 :param node: VPP node to add SAD entry on.
280 :param sad_id: SAD entry ID.
281 :param spi: Security Parameter Index of this SAD entry.
282 :param crypto_alg: The encryption algorithm name.
283 :param crypto_key: The encryption key string.
284 :param integ_alg: The integrity algorithm name.
285 :param integ_key: The integrity key string.
286 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
287 specified ESP transport mode is used.
288 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
289 not specified ESP transport mode is used.
293 :type crypto_alg: CryptoAlg
294 :type crypto_key: str
295 :type integ_alg: IntegAlg
297 :type tunnel_src: str
298 :type tunnel_dst: str
301 length=len(crypto_key),
305 length=len(integ_key),
306 data=integ_key if integ_key else 0
309 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
310 if tunnel_src and tunnel_dst:
311 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
312 src_addr = ip_address(unicode(tunnel_src))
313 dst_addr = ip_address(unicode(tunnel_dst))
314 if src_addr.version == 6:
316 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
321 cmd = 'ipsec_sad_entry_add_del'
322 err_msg = 'Failed to add Security Association Database entry on ' \
323 'host {host}'.format(host=node['host'])
327 crypto_algorithm=crypto_alg.alg_int_repr,
329 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
332 tunnel_src=str(src_addr),
333 tunnel_dst=str(dst_addr),
334 protocol=int(IPsecProto.ESP)
340 with PapiSocketExecutor(node) as papi_exec:
341 papi_exec.add(cmd, **args).get_reply(err_msg)
344 def vpp_ipsec_add_sad_entries(
345 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
346 integ_alg=None, integ_key='', tunnel_src=None, tunnel_dst=None):
347 """Create multiple Security Association Database entries on VPP node.
349 :param node: VPP node to add SAD entry on.
350 :param n_entries: Number of SAD entries to be created.
351 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
353 :param spi: Security Parameter Index of first SAD entry. All subsequent
354 SAD entries will have spi incremented by 1.
355 :param crypto_alg: The encryption algorithm name.
356 :param crypto_key: The encryption key string.
357 :param integ_alg: The integrity algorithm name.
358 :param integ_key: The integrity key string.
359 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
360 specified ESP transport mode is used.
361 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
362 not specified ESP transport mode is used.
367 :type crypto_alg: CryptoAlg
368 :type crypto_key: str
369 :type integ_alg: IntegAlg
371 :type tunnel_src: str
372 :type tunnel_dst: str
374 if tunnel_src and tunnel_dst:
375 src_addr = ip_address(unicode(tunnel_src))
376 dst_addr = ip_address(unicode(tunnel_dst))
381 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
384 if int(n_entries) > 10:
385 tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.\
388 with open(tmp_filename, 'w') as tmp_file:
389 for i in xrange(n_entries):
391 'integ-alg {integ_alg} integ-key {integ_key}'.format(
392 integ_alg=integ_alg.alg_name,
393 integ_key=integ_key.encode('hex'))
394 if integ_alg else '')
396 'tunnel-src {laddr} tunnel-dst {raddr}'.format(
397 laddr=src_addr + i * addr_incr,
398 raddr=dst_addr + i * addr_incr)
399 if tunnel_src and tunnel_dst else '')
401 'exec ipsec sa add {sad_id} esp spi {spi} '
402 'crypto-alg {crypto_alg} crypto-key {crypto_key} '
403 '{integ} {tunnel}\n'.format(
406 crypto_alg=crypto_alg.alg_name,
407 crypto_key=crypto_key.encode('hex'),
412 vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
413 copy_on_execute=True)
414 os.remove(tmp_filename)
418 length=len(crypto_key),
422 length=len(integ_key),
423 data=integ_key if integ_key else 0
426 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
427 if tunnel_src and tunnel_dst:
428 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
429 if src_addr.version == 6:
431 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
433 cmd = 'ipsec_sad_entry_add_del'
434 err_msg = 'Failed to add Security Association Database entry on ' \
435 'host {host}'.format(host=node['host'])
440 crypto_algorithm=crypto_alg.alg_int_repr,
442 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
445 tunnel_src=str(src_addr),
446 tunnel_dst=str(dst_addr),
447 protocol=int(IPsecProto.ESP)
453 with PapiSocketExecutor(node) as papi_exec:
454 for i in xrange(n_entries):
455 args['entry']['sad_id'] = int(sad_id) + i
456 args['entry']['spi'] = int(spi) + i
457 args['entry']['tunnel_src'] = str(src_addr + i * addr_incr) \
458 if tunnel_src and tunnel_dst else src_addr
459 args['entry']['tunnel_dst'] = str(dst_addr + i * addr_incr) \
460 if tunnel_src and tunnel_dst else dst_addr
461 history = False if 1 < i < n_entries - 1 else True
462 papi_exec.add(cmd, history=history, **args)
463 papi_exec.get_replies(err_msg)
466 def vpp_ipsec_set_ip_route(
467 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
469 """Set IP address and route on interface.
471 :param node: VPP node to add config on.
472 :param n_tunnels: Number of tunnels to create.
473 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
474 :param traffic_addr: Traffic destination IP address to route.
475 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
476 :param interface: Interface key on node 1.
477 :param raddr_range: Mask specifying range of Policy selector Remote IP
478 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
482 :type tunnel_src: str
483 :type traffic_addr: str
484 :type tunnel_dst: str
486 :type raddr_range: int
488 laddr = ip_address(unicode(tunnel_src))
489 raddr = ip_address(unicode(tunnel_dst))
490 taddr = ip_address(unicode(traffic_addr))
491 addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
492 else 1 << (32 - raddr_range)
494 if int(n_tunnels) > 10:
495 tmp_filename = '/tmp/ipsec_set_ip.script'
497 with open(tmp_filename, 'w') as tmp_file:
498 for i in xrange(n_tunnels):
500 'exec set interface ip address {interface} '
501 '{laddr}/{laddr_l}\n'
502 'exec ip route add {taddr}/{taddr_l} via {raddr} '
503 '{interface}\n'.format(
504 interface=Topology.get_interface_name(
506 laddr=laddr + i * addr_incr,
508 raddr=raddr + i * addr_incr,
510 taddr_l=128 if taddr.version == 6 else 32))
513 vat.execute_script(tmp_filename, node, timeout=300, json_out=False,
514 copy_on_execute=True)
515 os.remove(tmp_filename)
518 cmd1 = 'sw_interface_add_del_address'
520 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
522 is_ipv6=1 if laddr.version == 6 else 0,
524 address_length=raddr_range,
527 cmd2 = 'ip_route_add_del'
533 err_msg = 'Failed to configure IP addresses and IP routes on ' \
534 'interface {ifc} on host {host}'.\
535 format(ifc=interface, host=node['host'])
537 with PapiSocketExecutor(node) as papi_exec:
538 for i in xrange(n_tunnels):
539 args1['address'] = getattr(laddr + i * addr_incr, 'packed')
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,
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),
964 is_ipv6=1 if if2_ip.version == 6 else 0,
966 address_length=96 if if2_ip.version == 6 else 24,
967 address=getattr(if2_ip - 1, 'packed'))
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,
975 is_ipv6=1 if if1_ip.version == 6 else 0,
977 address_length=128 if if1_ip.version == 6 else 32,
979 cmd2 = 'ipsec_tunnel_if_add_del'
986 crypto_alg=crypto_alg.alg_int_repr,
987 local_crypto_key_len=0,
988 local_crypto_key=None,
989 remote_crypto_key_len=0,
990 remote_crypto_key=None,
991 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
992 local_integ_key_len=0,
993 local_integ_key=None,
994 remote_integ_key_len=0,
995 remote_integ_key=None,
998 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
999 format(host=nodes['DUT1']['host'])
1000 ipsec_tunnels = list()
1003 for i in xrange(n_tunnels):
1005 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1008 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1009 args1['address'] = getattr(if1_ip + i * addr_incr, 'packed')
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'
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),
1073 is_ipv6=1 if if2_ip.version == 6 else 0,
1075 address_length=96 if if2_ip.version == 6 else 24,
1076 address=if2_ip.packed)
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'
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')