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'
528 route = IPUtil.compose_vpp_route_structure(
530 prefix_len=128 if taddr.version == 6 else 32,
539 err_msg = 'Failed to configure IP addresses and IP routes on ' \
540 'interface {ifc} on host {host}'.\
541 format(ifc=interface, host=node['host'])
543 with PapiSocketExecutor(node) as papi_exec:
544 for i in xrange(n_tunnels):
545 args1['address'] = getattr(laddr + i * addr_incr, 'packed')
546 args2['route']['prefix']['address']['un'] = \
547 IPUtil.union_addr(taddr + i)
548 args2['route']['paths'][0]['nh']['address'] = \
549 IPUtil.union_addr(raddr + i * addr_incr)
550 history = False if 1 < i < n_tunnels - 1 else True
551 papi_exec.add(cmd1, history=history, **args1).\
552 add(cmd2, history=history, **args2)
553 papi_exec.get_replies(err_msg)
556 def vpp_ipsec_add_spd(node, spd_id):
557 """Create Security Policy Database on the VPP node.
559 :param node: VPP node to add SPD on.
560 :param spd_id: SPD ID.
564 cmd = 'ipsec_spd_add_del'
565 err_msg = 'Failed to add Security Policy Database on host {host}'.\
566 format(host=node['host'])
571 with PapiSocketExecutor(node) as papi_exec:
572 papi_exec.add(cmd, **args).get_reply(err_msg)
575 def vpp_ipsec_spd_add_if(node, spd_id, interface):
576 """Add interface to the Security Policy Database.
578 :param node: VPP node.
579 :param spd_id: SPD ID to add interface on.
580 :param interface: Interface name or sw_if_index.
583 :type interface: str or int
585 cmd = 'ipsec_interface_add_del_spd'
586 err_msg = 'Failed to add interface {ifc} to Security Policy Database ' \
587 '{spd} on host {host}'.\
588 format(ifc=interface, spd=spd_id, host=node['host'])
591 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
594 with PapiSocketExecutor(node) as papi_exec:
595 papi_exec.add(cmd, **args).get_reply(err_msg)
598 def vpp_ipsec_policy_add(
599 node, spd_id, priority, action, inbound=True, sa_id=None,
600 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
601 rport_range=None, is_ipv6=False):
602 """Create Security Policy Database entry on the VPP node.
604 :param node: VPP node to add SPD entry on.
605 :param spd_id: SPD ID to add entry on.
606 :param priority: SPD entry priority, higher number = higher priority.
607 :param action: Policy action.
608 :param inbound: If True policy is for inbound traffic, otherwise
610 :param sa_id: SAD entry ID for protect action.
611 :param laddr_range: Policy selector local 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 raddr_range: Policy selector remote IPv4 or IPv6 address range in
615 format IP/prefix or IP/mask. If no mask is provided,
616 it's considered to be /32.
617 :param proto: Policy selector next layer protocol number.
618 :param lport_range: Policy selector local TCP/UDP port range in format
619 <port_start>-<port_end>.
620 :param rport_range: Policy selector remote TCP/UDP port range in format
621 <port_start>-<port_end>.
622 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
623 not defined so it will default to address ::/0, otherwise False.
627 :type action: PolicyAction
630 :type laddr_range: string
631 :type raddr_range: string
633 :type lport_range: string
634 :type rport_range: string
638 if laddr_range is None:
639 laddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
641 if raddr_range is None:
642 raddr_range = '::/0' if is_ipv6 else '0.0.0.0/0'
644 cmd = 'ipsec_spd_entry_add_del'
645 err_msg = 'Failed to add entry to Security Policy Database ' \
646 '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
650 priority=int(priority),
651 is_outbound=0 if inbound else 1,
652 sa_id=int(sa_id) if sa_id else 0,
653 policy=action.policy_int_repr,
654 protocol=int(proto) if proto else 0,
655 remote_address_start=IPUtil.create_ip_address_object(
656 ip_network(unicode(raddr_range), strict=False).network_address),
657 remote_address_stop=IPUtil.create_ip_address_object(
659 unicode(raddr_range), strict=False).broadcast_address),
660 local_address_start=IPUtil.create_ip_address_object(
662 unicode(laddr_range), strict=False).network_address),
663 local_address_stop=IPUtil.create_ip_address_object(
665 unicode(laddr_range), strict=False).broadcast_address),
666 remote_port_start=int(rport_range.split('-')[0]) if rport_range
668 remote_port_stop=int(rport_range.split('-')[1]) if rport_range
670 local_port_start=int(lport_range.split('-')[0]) if lport_range
672 local_port_stop=int(lport_range.split('-')[1]) if rport_range
679 with PapiSocketExecutor(node) as papi_exec:
680 papi_exec.add(cmd, **args).get_reply(err_msg)
683 def vpp_ipsec_spd_add_entries(
684 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip):
685 """Create multiple Security Policy Database entries on the VPP node.
687 :param node: VPP node to add SPD entries on.
688 :param n_entries: Number of SPD entries to be added.
689 :param spd_id: SPD ID to add entries on.
690 :param priority: SPD entries priority, higher number = higher priority.
691 :param inbound: If True policy is for inbound traffic, otherwise
693 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
694 SAD entry ID incremented by 1.
695 :param raddr_ip: Policy selector remote IPv4 start address for the first
696 entry. Remote IPv4 end address will be calculated depending on
697 raddr_range parameter. Each subsequent entry will have start address
698 next after IPv4 end address of previous entry.
705 :type raddr_ip: string
707 if int(n_entries) > 10:
708 tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.\
711 with open(tmp_filename, 'w') as tmp_file:
712 for i in xrange(n_entries):
713 raddr_s = ip_address(unicode(raddr_ip)) + i
714 raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1
716 'exec ipsec policy add spd {spd_id} '
717 'priority {priority} {direction} action protect '
718 'sa {sa_id} remote-ip-range {raddr_s} - {raddr_e} '
719 'local-ip-range 0.0.0.0 - 255.255.255.255\n'.
723 direction='inbound' if inbound else 'outbound',
727 tmp_file.write(tunnel)
728 VatExecutor().execute_script(
729 tmp_filename, node, timeout=300, json_out=False,
730 copy_on_execute=True)
731 os.remove(tmp_filename)
734 raddr_ip = ip_address(unicode(raddr_ip))
735 laddr_range = '::/0' if raddr_ip.version == 6 else '0.0.0.0/0'
737 cmd = 'ipsec_spd_entry_add_del'
738 err_msg = 'Failed to add entry to Security Policy Database ' \
739 '{spd} on host {host}'.format(spd=spd_id, host=node['host'])
743 priority=int(priority),
744 is_outbound=0 if inbound else 1,
745 sa_id=int(sa_id) if sa_id else 0,
746 policy=IPsecUtil.policy_action_protect().policy_int_repr,
748 remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
749 remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
750 local_address_start=IPUtil.create_ip_address_object(
751 ip_network(unicode(laddr_range), strict=False).network_address),
752 local_address_stop=IPUtil.create_ip_address_object(
754 unicode(laddr_range), strict=False).broadcast_address),
756 remote_port_stop=65535,
758 local_port_stop=65535
765 with PapiSocketExecutor(node) as papi_exec:
766 for i in xrange(n_entries):
767 args['entry']['remote_address_start']['un'] = \
768 IPUtil.union_addr(raddr_ip + i)
769 args['entry']['remote_address_stop']['un'] = \
770 IPUtil.union_addr(raddr_ip + i)
771 history = False if 1 < i < n_entries - 1 else True
772 papi_exec.add(cmd, history=history, **args)
773 papi_exec.get_replies(err_msg)
776 def vpp_ipsec_create_tunnel_interfaces(
777 nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
778 crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range):
779 """Create multiple IPsec tunnel interfaces between two VPP nodes.
781 :param nodes: VPP nodes to create tunnel interfaces.
782 :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
783 :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
784 :param if1_key: VPP node 1 interface key from topology file.
785 :param if2_key: VPP node 2 interface key from topology file.
786 :param n_tunnels: Number of tunnell interfaces to create.
787 :param crypto_alg: The encryption algorithm name.
788 :param integ_alg: The integrity algorithm name.
789 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
790 first tunnel in direction node1->node2.
791 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
792 first tunnel in direction node2->node1.
793 :param raddr_range: Mask specifying range of Policy selector Remote
794 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
795 and to 128 in case of IPv6.
797 :type if1_ip_addr: str
798 :type if2_ip_addr: str
802 :type crypto_alg: CryptoAlg
803 :type integ_alg: IntegAlg
804 :type raddr_ip1: string
805 :type raddr_ip2: string
806 :type raddr_range: int
808 n_tunnels = int(n_tunnels)
811 if1_ip = ip_address(unicode(if1_ip_addr))
812 if2_ip = ip_address(unicode(if2_ip_addr))
813 raddr_ip1 = ip_address(unicode(raddr_ip1))
814 raddr_ip2 = ip_address(unicode(raddr_ip2))
815 addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
816 else 1 << (32 - raddr_range)
819 tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config'
820 tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config'
822 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
824 'exec create loopback interface\n'
825 'exec set interface state loop0 up\n'
826 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
829 uifc=Topology.get_interface_name(
830 nodes['DUT1'], if1_key),
831 mask=96 if if2_ip.version == 6 else 24))
833 'exec set interface ip address {uifc} {iaddr}/{mask}\n'
836 uifc=Topology.get_interface_name(
837 nodes['DUT2'], if2_key),
838 mask=96 if if2_ip.version == 6 else 24))
839 for i in xrange(n_tunnels):
840 ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(
841 crypto_alg)).encode('hex')
843 ikey = gen_key(IPsecUtil.get_integ_alg_key_len(
844 integ_alg)).encode('hex')
846 'integ_alg {integ_alg} '
847 'local_integ_key {local_integ_key} '
848 'remote_integ_key {remote_integ_key} '
850 integ_alg=integ_alg.alg_name,
851 local_integ_key=ikey,
852 remote_integ_key=ikey))
856 'exec set interface ip address loop0 {laddr}/32\n'
857 'ipsec_tunnel_if_add_del '
858 'local_spi {local_spi} '
859 'remote_spi {remote_spi} '
860 'crypto_alg {crypto_alg} '
861 'local_crypto_key {local_crypto_key} '
862 'remote_crypto_key {remote_crypto_key} '
865 'remote_ip {raddr}\n'
868 remote_spi=spi_2 + i,
869 crypto_alg=crypto_alg.alg_name,
870 local_crypto_key=ckey,
871 remote_crypto_key=ckey,
873 laddr=if1_ip + i * addr_incr,
876 'ipsec_tunnel_if_add_del '
877 'local_spi {local_spi} '
878 'remote_spi {remote_spi} '
879 'crypto_alg {crypto_alg} '
880 'local_crypto_key {local_crypto_key} '
881 'remote_crypto_key {remote_crypto_key} '
884 'remote_ip {raddr}\n'
887 remote_spi=spi_1 + i,
888 crypto_alg=crypto_alg.alg_name,
889 local_crypto_key=ckey,
890 remote_crypto_key=ckey,
893 raddr=if1_ip + i * addr_incr))
895 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
896 copy_on_execute=True,
897 history=False if n_tunnels > 100 else True)
899 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
900 copy_on_execute=True,
901 history=False if n_tunnels > 100 else True)
905 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
907 'exec ip route add {raddr} via {uifc} {iaddr}\n'
909 raddr=ip_network(unicode(if1_ip_addr+'/8'), False),
911 uifc=Topology.get_interface_name(
912 nodes['DUT2'], if2_key)))
913 for i in xrange(n_tunnels):
915 'exec set interface unnumbered ipsec{i} use {uifc}\n'
916 'exec set interface state ipsec{i} up\n'
917 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
921 uifc=Topology.get_interface_name(nodes['DUT1'],
923 mask=128 if if2_ip.version == 6 else 32))
925 'exec set interface unnumbered ipsec{i} use {uifc}\n'
926 'exec set interface state ipsec{i} up\n'
927 'exec ip route add {taddr}/{mask} via ipsec{i}\n'
931 uifc=Topology.get_interface_name(nodes['DUT2'],
933 mask=128 if if2_ip.version == 6 else 32))
935 tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False,
936 copy_on_execute=True,
937 history=False if n_tunnels > 100 else True)
939 tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False,
940 copy_on_execute=True,
941 history=False if n_tunnels > 100 else True)
946 with PapiSocketExecutor(nodes['DUT1']) as papi_exec:
947 # Create loopback interface on DUT1, set it to up state
948 cmd1 = 'create_loopback'
949 args1 = dict(mac_address=0)
950 err_msg = 'Failed to create loopback interface on host {host}'.\
951 format(host=nodes['DUT1']['host'])
952 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
953 get_sw_if_index(err_msg)
954 cmd1 = 'sw_interface_set_flags'
956 sw_if_index=loop_sw_if_idx,
958 err_msg = 'Failed to set loopback interface state up on host ' \
959 '{host}'.format(host=nodes['DUT1']['host'])
960 papi_exec.add(cmd1, **args1).get_reply(err_msg)
961 # Set IP address on VPP node 1 interface
962 cmd1 = 'sw_interface_add_del_address'
964 sw_if_index=InterfaceUtil.get_interface_index(
965 nodes['DUT1'], if1_key),
967 is_ipv6=1 if if2_ip.version == 6 else 0,
969 address_length=96 if if2_ip.version == 6 else 24,
970 address=getattr(if2_ip - 1, 'packed'))
971 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
972 '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host'])
973 papi_exec.add(cmd1, **args1).get_reply(err_msg)
974 # Configure IPsec tunnel interfaces
976 sw_if_index=loop_sw_if_idx,
978 is_ipv6=1 if if1_ip.version == 6 else 0,
980 address_length=128 if if1_ip.version == 6 else 32,
982 cmd2 = 'ipsec_tunnel_if_add_del'
989 crypto_alg=crypto_alg.alg_int_repr,
990 local_crypto_key_len=0,
991 local_crypto_key=None,
992 remote_crypto_key_len=0,
993 remote_crypto_key=None,
994 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
995 local_integ_key_len=0,
996 local_integ_key=None,
997 remote_integ_key_len=0,
998 remote_integ_key=None,
1001 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\
1002 format(host=nodes['DUT1']['host'])
1003 ipsec_tunnels = list()
1006 for i in xrange(n_tunnels):
1008 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)))
1011 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)))
1012 args1['address'] = getattr(if1_ip + i * addr_incr, 'packed')
1013 args2['local_spi'] = spi_1 + i
1014 args2['remote_spi'] = spi_2 + i
1015 args2['local_ip'] = IPUtil.create_ip_address_object(
1016 if1_ip + i * addr_incr)
1017 args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip)
1018 args2['local_crypto_key_len'] = len(ckeys[i])
1019 args2['local_crypto_key'] = ckeys[i]
1020 args2['remote_crypto_key_len'] = len(ckeys[i])
1021 args2['remote_crypto_key'] = ckeys[i]
1023 args2['local_integ_key_len'] = len(ikeys[i])
1024 args2['local_integ_key'] = ikeys[i]
1025 args2['remote_integ_key_len'] = len(ikeys[i])
1026 args2['remote_integ_key'] = ikeys[i]
1027 history = False if 1 < i < n_tunnels - 1 else True
1028 papi_exec.add(cmd1, history=history, **args1).\
1029 add(cmd2, history=history, **args2)
1030 replies = papi_exec.get_replies(err_msg)
1031 for reply in replies:
1032 if 'sw_if_index' in reply:
1033 ipsec_tunnels.append(reply["sw_if_index"])
1034 # Configure IP routes
1035 cmd1 = 'sw_interface_set_unnumbered'
1038 sw_if_index=InterfaceUtil.get_interface_index(
1039 nodes['DUT1'], if1_key),
1040 unnumbered_sw_if_index=0
1042 cmd2 = 'sw_interface_set_flags'
1046 cmd3 = 'ip_route_add_del'
1047 route = IPUtil.compose_vpp_route_structure(
1048 nodes['DUT1'], raddr_ip2.compressed,
1049 prefix_len=128 if raddr_ip2.version == 6 else 32,
1057 err_msg = 'Failed to add IP routes on host {host}'.format(
1058 host=nodes['DUT1']['host'])
1059 for i in xrange(n_tunnels):
1060 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1061 args2['sw_if_index'] = ipsec_tunnels[i]
1062 args3['route']['prefix']['address']['un'] = \
1063 IPUtil.union_addr(raddr_ip2 + i)
1064 args3['route']['paths'][0]['sw_if_index'] = \
1066 history = False if 1 < i < n_tunnels - 1 else True
1067 papi_exec.add(cmd1, history=history, **args1).\
1068 add(cmd2, history=history, **args2).\
1069 add(cmd3, history=history, **args3)
1070 papi_exec.get_replies(err_msg)
1072 with PapiSocketExecutor(nodes['DUT2']) as papi_exec:
1073 # Set IP address on VPP node 2 interface
1074 cmd1 = 'sw_interface_add_del_address'
1076 sw_if_index=InterfaceUtil.get_interface_index(
1077 nodes['DUT2'], if2_key),
1079 is_ipv6=1 if if2_ip.version == 6 else 0,
1081 address_length=96 if if2_ip.version == 6 else 24,
1082 address=if2_ip.packed)
1083 err_msg = 'Failed to set IP address on interface {ifc} on host ' \
1084 '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host'])
1085 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1086 # Configure IPsec tunnel interfaces
1087 cmd2 = 'ipsec_tunnel_if_add_del'
1090 local_ip=IPUtil.create_ip_address_object(if2_ip),
1094 crypto_alg=crypto_alg.alg_int_repr,
1095 local_crypto_key_len=0,
1096 local_crypto_key=None,
1097 remote_crypto_key_len=0,
1098 remote_crypto_key=None,
1099 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1100 local_integ_key_len=0,
1101 local_integ_key=None,
1102 remote_integ_key_len=0,
1103 remote_integ_key=None,
1106 err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \
1107 format(host=nodes['DUT2']['host'])
1108 ipsec_tunnels = list()
1109 for i in xrange(n_tunnels):
1110 args2['local_spi'] = spi_2 + i
1111 args2['remote_spi'] = spi_1 + i
1112 args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip)
1113 args2['remote_ip'] = IPUtil.create_ip_address_object(
1114 if1_ip + i * addr_incr)
1115 args2['local_crypto_key_len'] = len(ckeys[i])
1116 args2['local_crypto_key'] = ckeys[i]
1117 args2['remote_crypto_key_len'] = len(ckeys[i])
1118 args2['remote_crypto_key'] = ckeys[i]
1120 args2['local_integ_key_len'] = len(ikeys[i])
1121 args2['local_integ_key'] = ikeys[i]
1122 args2['remote_integ_key_len'] = len(ikeys[i])
1123 args2['remote_integ_key'] = ikeys[i]
1124 history = False if 1 < i < n_tunnels - 1 else True
1125 papi_exec.add(cmd2, history=history, **args2)
1126 replies = papi_exec.get_replies(err_msg)
1127 for reply in replies:
1128 if 'sw_if_index' in reply:
1129 ipsec_tunnels.append(reply["sw_if_index"])
1130 # Configure IP routes
1131 cmd1 = 'ip_route_add_del'
1132 route = IPUtil.compose_vpp_route_structure(
1133 nodes['DUT2'], if1_ip.compressed,
1134 prefix_len=32 if if1_ip.version == 6 else 8,
1136 gateway=(if2_ip - 1).compressed
1143 papi_exec.add(cmd1, **args1)
1144 cmd1 = 'sw_interface_set_unnumbered'
1147 sw_if_index=InterfaceUtil.get_interface_index(
1148 nodes['DUT2'], if2_key),
1149 unnumbered_sw_if_index=0
1151 cmd2 = 'sw_interface_set_flags'
1155 cmd3 = 'ip_route_add_del'
1156 route = IPUtil.compose_vpp_route_structure(
1157 nodes['DUT2'], raddr_ip1.compressed,
1158 prefix_len=128 if raddr_ip1.version == 6 else 32,
1166 err_msg = 'Failed to add IP routes on host {host}'.format(
1167 host=nodes['DUT2']['host'])
1168 for i in xrange(n_tunnels):
1169 args1['unnumbered_sw_if_index'] = ipsec_tunnels[i]
1170 args2['sw_if_index'] = ipsec_tunnels[i]
1171 args3['route']['prefix']['address']['un'] = \
1172 IPUtil.union_addr(raddr_ip1 + i)
1173 args3['route']['paths'][0]['sw_if_index'] = \
1175 history = False if 1 < i < n_tunnels - 1 else True
1176 papi_exec.add(cmd1, history=history, **args1). \
1177 add(cmd2, history=history, **args2). \
1178 add(cmd3, history=history, **args3)
1179 papi_exec.get_replies(err_msg)
1182 def vpp_ipsec_add_multiple_tunnels(
1183 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1184 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1185 """Create multiple IPsec tunnels between two VPP nodes.
1187 :param nodes: VPP nodes to create tunnels.
1188 :param interface1: Interface name or sw_if_index on node 1.
1189 :param interface2: Interface name or sw_if_index on node 2.
1190 :param n_tunnels: Number of tunnels to create.
1191 :param crypto_alg: The encryption algorithm name.
1192 :param integ_alg: The integrity algorithm name.
1193 :param tunnel_ip1: Tunnel node1 IPv4 address.
1194 :param tunnel_ip2: Tunnel node2 IPv4 address.
1195 :param raddr_ip1: Policy selector remote IPv4 start address for the
1196 first tunnel in direction node1->node2.
1197 :param raddr_ip2: Policy selector remote IPv4 start address for the
1198 first tunnel in direction node2->node1.
1199 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1200 addresses. Valid values are from 1 to 32.
1202 :type interface1: str or int
1203 :type interface2: str or int
1204 :type n_tunnels: int
1205 :type crypto_alg: CryptoAlg
1206 :type integ_alg: IntegAlg
1207 :type tunnel_ip1: str
1208 :type tunnel_ip2: str
1209 :type raddr_ip1: string
1210 :type raddr_ip2: string
1211 :type raddr_range: int
1221 crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1222 integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \
1223 if integ_alg else ''
1225 IPsecUtil.vpp_ipsec_set_ip_route(
1226 nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1227 interface1, raddr_range)
1228 IPsecUtil.vpp_ipsec_set_ip_route(
1229 nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1230 interface2, raddr_range)
1232 IPsecUtil.vpp_ipsec_add_spd(
1233 nodes['DUT1'], spd_id)
1234 IPsecUtil.vpp_ipsec_spd_add_if(
1235 nodes['DUT1'], spd_id, interface1)
1236 IPsecUtil.vpp_ipsec_policy_add(
1237 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1238 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1239 IPsecUtil.vpp_ipsec_policy_add(
1240 nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1241 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1243 IPsecUtil.vpp_ipsec_add_spd(
1244 nodes['DUT2'], spd_id)
1245 IPsecUtil.vpp_ipsec_spd_add_if(
1246 nodes['DUT2'], spd_id, interface2)
1247 IPsecUtil.vpp_ipsec_policy_add(
1248 nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1249 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1250 IPsecUtil.vpp_ipsec_policy_add(
1251 nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1252 proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8')
1254 IPsecUtil.vpp_ipsec_add_sad_entries(
1255 nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1256 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1258 IPsecUtil.vpp_ipsec_spd_add_entries(
1259 nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2)
1261 IPsecUtil.vpp_ipsec_add_sad_entries(
1262 nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1263 integ_alg, integ_key, tunnel_ip1, tunnel_ip2)
1265 IPsecUtil.vpp_ipsec_spd_add_entries(
1266 nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2)
1268 IPsecUtil.vpp_ipsec_add_sad_entries(
1269 nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1270 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1272 IPsecUtil.vpp_ipsec_spd_add_entries(
1273 nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1)
1275 IPsecUtil.vpp_ipsec_add_sad_entries(
1276 nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1277 integ_alg, integ_key, tunnel_ip2, tunnel_ip1)
1279 IPsecUtil.vpp_ipsec_spd_add_entries(
1280 nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1)
1283 def vpp_ipsec_show(node):
1284 """Run "show ipsec" debug CLI command.
1286 :param node: Node to run command on.
1289 PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')