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