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 enum import Enum, IntEnum
20 from random import choice
21 from string import ascii_letters
23 from ipaddress import ip_network, ip_address
25 from resources.libraries.python.IPUtil import IPUtil
26 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
28 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
29 from resources.libraries.python.topology import Topology
30 from resources.libraries.python.VatExecutor import VatExecutor
34 """Generate random string as a key.
36 :param length: Length of generated payload.
38 :returns: The generated payload.
42 choice(ascii_letters) for _ in range(length)
43 ).encode(encoding=u"utf-8")
46 class PolicyAction(Enum):
48 BYPASS = (u"bypass", 0)
49 DISCARD = (u"discard", 1)
50 PROTECT = (u"protect", 3)
52 def __init__(self, policy_name, policy_int_repr):
53 self.policy_name = policy_name
54 self.policy_int_repr = policy_int_repr
57 class CryptoAlg(Enum):
58 """Encryption algorithms."""
59 AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
60 AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
61 AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
62 AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
64 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
65 self.alg_name = alg_name
66 self.alg_int_repr = alg_int_repr
67 self.scapy_name = scapy_name
68 self.key_len = key_len
72 """Integrity algorithm."""
73 SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
74 SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
76 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
77 self.alg_name = alg_name
78 self.alg_int_repr = alg_int_repr
79 self.scapy_name = scapy_name
80 self.key_len = key_len
83 class IPsecProto(IntEnum):
89 class IPsecSadFlags(IntEnum):
90 """IPsec Security Association Database flags."""
91 IPSEC_API_SAD_FLAG_NONE = 0
92 IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
93 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
97 """IPsec utilities."""
100 def policy_action_bypass():
101 """Return policy action bypass.
103 :returns: PolicyAction enum BYPASS object.
106 return PolicyAction.BYPASS
109 def policy_action_discard():
110 """Return policy action discard.
112 :returns: PolicyAction enum DISCARD object.
115 return PolicyAction.DISCARD
118 def policy_action_protect():
119 """Return policy action protect.
121 :returns: PolicyAction enum PROTECT object.
124 return PolicyAction.PROTECT
127 def crypto_alg_aes_cbc_128():
128 """Return encryption algorithm aes-cbc-128.
130 :returns: CryptoAlg enum AES_CBC_128 object.
133 return CryptoAlg.AES_CBC_128
136 def crypto_alg_aes_cbc_256():
137 """Return encryption algorithm aes-cbc-256.
139 :returns: CryptoAlg enum AES_CBC_256 object.
142 return CryptoAlg.AES_CBC_256
145 def crypto_alg_aes_gcm_128():
146 """Return encryption algorithm aes-gcm-128.
148 :returns: CryptoAlg enum AES_GCM_128 object.
151 return CryptoAlg.AES_GCM_128
154 def crypto_alg_aes_gcm_256():
155 """Return encryption algorithm aes-gcm-256.
157 :returns: CryptoAlg enum AES_GCM_128 object.
160 return CryptoAlg.AES_GCM_256
163 def get_crypto_alg_key_len(crypto_alg):
164 """Return encryption algorithm key length.
166 :param crypto_alg: Encryption algorithm.
167 :type crypto_alg: CryptoAlg
168 :returns: Key length.
171 return crypto_alg.key_len
174 def get_crypto_alg_scapy_name(crypto_alg):
175 """Return encryption algorithm scapy name.
177 :param crypto_alg: Encryption algorithm.
178 :type crypto_alg: CryptoAlg
179 :returns: Algorithm scapy name.
182 return crypto_alg.scapy_name
185 def integ_alg_sha_256_128():
186 """Return integrity algorithm SHA-256-128.
188 :returns: IntegAlg enum SHA_256_128 object.
191 return IntegAlg.SHA_256_128
194 def integ_alg_sha_512_256():
195 """Return integrity algorithm SHA-512-256.
197 :returns: IntegAlg enum SHA_512_256 object.
200 return IntegAlg.SHA_512_256
203 def get_integ_alg_key_len(integ_alg):
204 """Return integrity algorithm key length.
206 :param integ_alg: Integrity algorithm.
207 :type integ_alg: IntegAlg
208 :returns: Key length.
211 return integ_alg.key_len
214 def get_integ_alg_scapy_name(integ_alg):
215 """Return integrity algorithm scapy name.
217 :param integ_alg: Integrity algorithm.
218 :type integ_alg: IntegAlg
219 :returns: Algorithm scapy name.
222 return integ_alg.scapy_name
225 def ipsec_proto_esp():
226 """Return IPSec protocol ESP.
228 :returns: IPsecProto enum ESP object.
231 return int(IPsecProto.ESP)
234 def ipsec_proto_ah():
235 """Return IPSec protocol AH.
237 :returns: IPsecProto enum AH object.
240 return int(IPsecProto.SEC_AH)
243 def vpp_ipsec_select_backend(node, protocol, index=1):
244 """Select IPsec backend.
246 :param node: VPP node to select IPsec backend on.
247 :param protocol: IPsec protocol.
248 :param index: Backend index.
250 :type protocol: IPsecProto
252 :raises RuntimeError: If failed to select IPsec backend or if no API
255 cmd = u"ipsec_select_backend"
256 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
261 with PapiSocketExecutor(node) as papi_exec:
262 papi_exec.add(cmd, **args).get_reply(err_msg)
265 def vpp_ipsec_backend_dump(node):
266 """Dump IPsec backends.
268 :param node: VPP node to dump IPsec backend on.
271 err_msg = f"Failed to dump IPsec backends on host {node[u'host']}"
272 with PapiSocketExecutor(node) as papi_exec:
273 papi_exec.add(u"ipsec_backend_dump").get_details(err_msg)
276 def vpp_ipsec_add_sad_entry(
277 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
278 integ_key=u"", tunnel_src=None, tunnel_dst=None):
279 """Create Security Association Database entry on the VPP node.
281 :param node: VPP node to add SAD entry on.
282 :param sad_id: SAD entry ID.
283 :param spi: Security Parameter Index of this SAD entry.
284 :param crypto_alg: The encryption algorithm name.
285 :param crypto_key: The encryption key string.
286 :param integ_alg: The integrity algorithm name.
287 :param integ_key: The integrity key string.
288 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
289 specified ESP transport mode is used.
290 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
291 not specified ESP transport mode is used.
295 :type crypto_alg: CryptoAlg
296 :type crypto_key: str
297 :type integ_alg: IntegAlg
299 :type tunnel_src: str
300 :type tunnel_dst: str
302 if isinstance(crypto_key, str):
303 crypto_key = crypto_key.encode(encoding=u"utf-8")
304 if isinstance(integ_key, str):
305 integ_key = integ_key.encode(encoding=u"utf-8")
307 length=len(crypto_key),
311 length=len(integ_key),
312 data=integ_key if integ_key else 0
315 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
316 if tunnel_src and tunnel_dst:
317 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
318 src_addr = ip_address(tunnel_src)
319 dst_addr = ip_address(tunnel_dst)
320 if src_addr.version == 6:
322 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
327 cmd = u"ipsec_sad_entry_add_del"
328 err_msg = f"Failed to add Security Association Database entry " \
329 f"on host {node[u'host']}"
333 crypto_algorithm=crypto_alg.alg_int_repr,
335 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
338 tunnel_src=str(src_addr),
339 tunnel_dst=str(dst_addr),
340 protocol=int(IPsecProto.ESP)
346 with PapiSocketExecutor(node) as papi_exec:
347 papi_exec.add(cmd, **args).get_reply(err_msg)
350 def vpp_ipsec_add_sad_entries(
351 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
352 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
353 """Create multiple Security Association Database entries on VPP node.
355 :param node: VPP node to add SAD entry on.
356 :param n_entries: Number of SAD entries to be created.
357 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
359 :param spi: Security Parameter Index of first SAD entry. All subsequent
360 SAD entries will have spi incremented by 1.
361 :param crypto_alg: The encryption algorithm name.
362 :param crypto_key: The encryption key string.
363 :param integ_alg: The integrity algorithm name.
364 :param integ_key: The integrity key string.
365 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
366 specified ESP transport mode is used.
367 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
368 not specified ESP transport mode is used.
373 :type crypto_alg: CryptoAlg
374 :type crypto_key: str
375 :type integ_alg: IntegAlg
377 :type tunnel_src: str
378 :type tunnel_dst: str
380 if isinstance(crypto_key, str):
381 crypto_key = crypto_key.encode(encoding=u"utf-8")
382 if isinstance(integ_key, str):
383 integ_key = integ_key.encode(encoding=u"utf-8")
384 if tunnel_src and tunnel_dst:
385 src_addr = ip_address(tunnel_src)
386 dst_addr = ip_address(tunnel_dst)
391 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
394 if int(n_entries) > 10:
395 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
397 with open(tmp_filename, 'w') as tmp_file:
398 for i in range(n_entries):
399 integ = f"integ-alg {integ_alg.alg_name} " \
400 f"integ-key {integ_key.hex()}" \
401 if integ_alg else u""
402 tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
403 f"tunnel-dst {dst_addr + i * addr_incr}" \
404 if tunnel_src and tunnel_dst else u""
405 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
406 f"crypto-alg {crypto_alg.alg_name} " \
407 f"crypto-key {crypto_key.hex()} " \
408 f"{integ} {tunnel}\n"
412 tmp_filename, node, timeout=300, json_out=False,
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
435 cmd = u"ipsec_sad_entry_add_del"
436 err_msg = f"Failed to add Security Association Database entry " \
437 f"on host {node[u'host']}"
442 crypto_algorithm=crypto_alg.alg_int_repr,
444 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
447 tunnel_src=str(src_addr),
448 tunnel_dst=str(dst_addr),
449 protocol=int(IPsecProto.ESP)
455 with PapiSocketExecutor(node) as papi_exec:
456 for i in range(n_entries):
457 args[u"entry"][u"sad_id"] = int(sad_id) + i
458 args[u"entry"][u"spi"] = int(spi) + i
459 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
460 if tunnel_src and tunnel_dst else src_addr
461 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
462 if tunnel_src and tunnel_dst else dst_addr
463 history = bool(not 1 < i < n_entries - 2)
464 papi_exec.add(cmd, history=history, **args)
465 papi_exec.get_replies(err_msg)
468 def vpp_ipsec_set_ip_route(
469 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
471 """Set IP address and route on interface.
473 :param node: VPP node to add config on.
474 :param n_tunnels: Number of tunnels to create.
475 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
476 :param traffic_addr: Traffic destination IP address to route.
477 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
478 :param interface: Interface key on node 1.
479 :param raddr_range: Mask specifying range of Policy selector Remote IP
480 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
484 :type tunnel_src: str
485 :type traffic_addr: str
486 :type tunnel_dst: str
488 :type raddr_range: int
490 laddr = ip_address(tunnel_src)
491 raddr = ip_address(tunnel_dst)
492 taddr = ip_address(traffic_addr)
493 addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
494 else 1 << (32 - raddr_range)
496 if int(n_tunnels) > 10:
497 tmp_filename = u"/tmp/ipsec_set_ip.script"
499 with open(tmp_filename, 'w') as tmp_file:
500 if_name = Topology.get_interface_name(node, interface)
501 for i in range(n_tunnels):
502 conf = f"exec set interface ip address {if_name} " \
503 f"{laddr + i * addr_incr}/{raddr_range}\n" \
504 f"exec ip route add {taddr + i}/" \
505 f"{128 if taddr.version == 6 else 32} " \
506 f"via {raddr + i * addr_incr} {if_name}\n"
510 tmp_filename, node, timeout=300, json_out=False,
513 os.remove(tmp_filename)
516 cmd1 = u"sw_interface_add_del_address"
518 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
523 cmd2 = u"ip_route_add_del"
529 err_msg = f"Failed to configure IP addresses and IP routes " \
530 f"on interface {interface} on host {node[u'host']}"
532 with PapiSocketExecutor(node) as papi_exec:
533 for i in range(n_tunnels):
534 args1[u"prefix"] = IPUtil.create_prefix_object(
535 laddr + i * addr_incr, raddr_range
537 args2[u"route"] = IPUtil.compose_vpp_route_structure(
539 prefix_len=128 if taddr.version == 6 else 32,
540 interface=interface, gateway=raddr + i * addr_incr
542 history = bool(not 1 < i < n_tunnels - 2)
543 papi_exec.add(cmd1, history=history, **args1).\
544 add(cmd2, history=history, **args2)
545 papi_exec.get_replies(err_msg)
548 def vpp_ipsec_add_spd(node, spd_id):
549 """Create Security Policy Database on the VPP node.
551 :param node: VPP node to add SPD on.
552 :param spd_id: SPD ID.
556 cmd = u"ipsec_spd_add_del"
557 err_msg = f"Failed to add Security Policy Database " \
558 f"on host {node[u'host']}"
563 with PapiSocketExecutor(node) as papi_exec:
564 papi_exec.add(cmd, **args).get_reply(err_msg)
567 def vpp_ipsec_spd_add_if(node, spd_id, interface):
568 """Add interface to the Security Policy Database.
570 :param node: VPP node.
571 :param spd_id: SPD ID to add interface on.
572 :param interface: Interface name or sw_if_index.
575 :type interface: str or int
577 cmd = u"ipsec_interface_add_del_spd"
578 err_msg = f"Failed to add interface {interface} to Security Policy " \
579 f"Database {spd_id} on host {node[u'host']}"
582 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
585 with PapiSocketExecutor(node) as papi_exec:
586 papi_exec.add(cmd, **args).get_reply(err_msg)
589 def vpp_ipsec_policy_add(
590 node, spd_id, priority, action, inbound=True, sa_id=None,
591 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
592 rport_range=None, is_ipv6=False):
593 """Create Security Policy Database entry on the VPP node.
595 :param node: VPP node to add SPD entry on.
596 :param spd_id: SPD ID to add entry on.
597 :param priority: SPD entry priority, higher number = higher priority.
598 :param action: Policy action.
599 :param inbound: If True policy is for inbound traffic, otherwise
601 :param sa_id: SAD entry ID for protect action.
602 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
603 format IP/prefix or IP/mask. If no mask is provided,
604 it's considered to be /32.
605 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
606 format IP/prefix or IP/mask. If no mask is provided,
607 it's considered to be /32.
608 :param proto: Policy selector next layer protocol number.
609 :param lport_range: Policy selector local TCP/UDP port range in format
610 <port_start>-<port_end>.
611 :param rport_range: Policy selector remote TCP/UDP port range in format
612 <port_start>-<port_end>.
613 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
614 not defined so it will default to address ::/0, otherwise False.
618 :type action: PolicyAction
621 :type laddr_range: string
622 :type raddr_range: string
624 :type lport_range: string
625 :type rport_range: string
628 if laddr_range is None:
629 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
631 if raddr_range is None:
632 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
634 cmd = u"ipsec_spd_entry_add_del"
635 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
636 f"on host {node[u'host']}"
640 priority=int(priority),
641 is_outbound=0 if inbound else 1,
642 sa_id=int(sa_id) if sa_id else 0,
643 policy=action.policy_int_repr,
644 protocol=int(proto) if proto else 0,
645 remote_address_start=IPUtil.create_ip_address_object(
646 ip_network(raddr_range, strict=False).network_address
648 remote_address_stop=IPUtil.create_ip_address_object(
649 ip_network(raddr_range, strict=False).broadcast_address
651 local_address_start=IPUtil.create_ip_address_object(
652 ip_network(laddr_range, strict=False).network_address
654 local_address_stop=IPUtil.create_ip_address_object(
655 ip_network(laddr_range, strict=False).broadcast_address
657 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
659 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
661 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
663 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
670 with PapiSocketExecutor(node) as papi_exec:
671 papi_exec.add(cmd, **args).get_reply(err_msg)
674 def vpp_ipsec_spd_add_entries(
675 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
677 """Create multiple Security Policy Database entries on the VPP node.
679 :param node: VPP node to add SPD entries on.
680 :param n_entries: Number of SPD entries to be added.
681 :param spd_id: SPD ID to add entries on.
682 :param priority: SPD entries priority, higher number = higher priority.
683 :param inbound: If True policy is for inbound traffic, otherwise
685 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
686 SAD entry ID incremented by 1.
687 :param raddr_ip: Policy selector remote IPv4 start address for the first
688 entry. Remote IPv4 end address will be calculated depending on
689 raddr_range parameter. Each subsequent entry will have start address
690 next after IPv4 end address of previous entry.
691 :param raddr_range: Required IP addres range.
699 :type raddr_range: int
701 raddr_ip = ip_address(raddr_ip)
702 if int(n_entries) > 10:
703 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
705 with open(tmp_filename, 'w') as tmp_file:
706 for i in range(n_entries):
707 direction = u'inbound' if inbound else u'outbound'
708 tunnel = f"exec ipsec policy add spd {spd_id} " \
709 f"priority {priority} {direction} " \
710 f"action protect sa {sa_id+i} " \
711 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
712 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
713 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
714 tmp_file.write(tunnel)
715 VatExecutor().execute_script(
716 tmp_filename, node, timeout=300, json_out=False,
719 os.remove(tmp_filename)
722 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
724 cmd = u"ipsec_spd_entry_add_del"
725 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
726 f"on host {node[u'host']}"
730 priority=int(priority),
731 is_outbound=0 if inbound else 1,
732 sa_id=int(sa_id) if sa_id else 0,
733 policy=IPsecUtil.policy_action_protect().policy_int_repr,
735 remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
736 remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
737 local_address_start=IPUtil.create_ip_address_object(
738 ip_network(laddr_range, strict=False).network_address
740 local_address_stop=IPUtil.create_ip_address_object(
741 ip_network(laddr_range, strict=False).broadcast_address
744 remote_port_stop=65535,
746 local_port_stop=65535
753 with PapiSocketExecutor(node) as papi_exec:
754 for i in range(n_entries):
755 args[u"entry"][u"remote_address_start"][u"un"] = \
756 IPUtil.union_addr(raddr_ip + i)
757 args[u"entry"][u"remote_address_stop"][u"un"] = \
758 IPUtil.union_addr(raddr_ip + i)
759 history = bool(not 1 < i < n_entries - 2)
760 papi_exec.add(cmd, history=history, **args)
761 papi_exec.get_replies(err_msg)
764 def vpp_ipsec_create_tunnel_interfaces(
765 nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
766 crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range):
767 """Create multiple IPsec tunnel interfaces between two VPP nodes.
769 :param nodes: VPP nodes to create tunnel interfaces.
770 :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
771 :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
772 :param if1_key: VPP node 1 interface key from topology file.
773 :param if2_key: VPP node 2 interface key from topology file.
774 :param n_tunnels: Number of tunnel interfaces to create.
775 :param crypto_alg: The encryption algorithm name.
776 :param integ_alg: The integrity algorithm name.
777 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
778 first tunnel in direction node1->node2.
779 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
780 first tunnel in direction node2->node1.
781 :param raddr_range: Mask specifying range of Policy selector Remote
782 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
783 and to 128 in case of IPv6.
785 :type if1_ip_addr: str
786 :type if2_ip_addr: str
790 :type crypto_alg: CryptoAlg
791 :type integ_alg: IntegAlg
792 :type raddr_ip1: string
793 :type raddr_ip2: string
794 :type raddr_range: int
796 n_tunnels = int(n_tunnels)
799 if1_ip = ip_address(if1_ip_addr)
800 if2_ip = ip_address(if2_ip_addr)
801 raddr_ip1 = ip_address(raddr_ip1)
802 raddr_ip2 = ip_address(raddr_ip2)
803 addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
804 else 1 << (32 - raddr_range)
807 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
808 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
809 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
810 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
811 mask = 96 if if2_ip.version == 6 else 24
812 mask2 = 128 if if2_ip.version == 6 else 32
814 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
815 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
817 f"exec create loopback interface\n"
818 f"exec set interface state loop0 up\n"
819 f"exec set interface ip address "
820 f"{if1_n} {if2_ip - 1}/{mask}\n"
821 f"exec set ip arp {if1_n} {if2_ip}/{mask2} {rmac} static\n"
824 f"exec set interface ip address {if2_n} {if2_ip}/{mask}\n"
826 for i in range(n_tunnels):
828 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
832 IPsecUtil.get_integ_alg_key_len(integ_alg)
834 integ = f"integ_alg {integ_alg.alg_name} " \
835 f"local_integ_key {ikey} remote_integ_key {ikey} "
839 f"exec set interface ip address loop0 "
840 f"{if1_ip + i * addr_incr}/32\n"
841 f"ipsec_tunnel_if_add_del "
842 f"local_spi {spi_1 + i} remote_spi {spi_2 + i} "
843 f"crypto_alg {crypto_alg.alg_name} "
844 f"local_crypto_key {ckey} remote_crypto_key {ckey} "
846 f"local_ip {if1_ip + i * addr_incr} "
847 f"remote_ip {if2_ip}\n"
850 f"ipsec_tunnel_if_add_del "
851 f"local_spi {spi_2 + i} remote_spi {spi_1 + i} "
852 f"crypto_alg {crypto_alg.alg_name} "
853 f"local_crypto_key {ckey} remote_crypto_key {ckey} "
855 f"local_ip {if2_ip} "
856 f"remote_ip {if1_ip + i * addr_incr}\n"
859 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
860 copy_on_execute=True,
861 history=bool(n_tunnels < 100)
864 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
865 copy_on_execute=True,
866 history=bool(n_tunnels < 100)
871 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
872 raddr = ip_network(if1_ip_addr + u"/8", False)
874 f"exec ip route add {raddr} via {if2_n} {if2_ip - 1}\n"
876 for i in range(n_tunnels):
878 f"exec set interface unnumbered ipsec{i} use {if1_n}\n"
879 f"exec set interface state ipsec{i} up\n"
880 f"exec ip route add {raddr_ip2 + i}/{mask2} "
884 f"exec set interface unnumbered ipsec{i} use {if2_n}\n"
885 f"exec set interface state ipsec{i} up\n"
886 f"exec ip route add {raddr_ip1 + i}/{mask2} "
890 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
891 copy_on_execute=True,
892 history=bool(n_tunnels < 100)
895 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
896 copy_on_execute=True,
897 history=bool(n_tunnels < 100)
903 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
904 # Create loopback interface on DUT1, set it to up state
905 cmd1 = u"create_loopback"
909 err_msg = f"Failed to create loopback interface " \
910 f"on host {nodes[u'DUT1'][u'host']}"
911 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
912 get_sw_if_index(err_msg)
913 cmd1 = u"sw_interface_set_flags"
915 sw_if_index=loop_sw_if_idx,
916 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
918 err_msg = f"Failed to set loopback interface state up " \
919 f"on host {nodes[u'DUT1'][u'host']}"
920 papi_exec.add(cmd1, **args1).get_reply(err_msg)
921 # Set IP address on VPP node 1 interface
922 cmd1 = u"sw_interface_add_del_address"
924 sw_if_index=InterfaceUtil.get_interface_index(
925 nodes[u"DUT1"], if1_key
929 prefix=IPUtil.create_prefix_object(
930 if2_ip - 1, 96 if if2_ip.version == 6 else 24
933 err_msg = f"Failed to set IP address on interface {if1_key} " \
934 f"on host {nodes[u'DUT1'][u'host']}"
935 papi_exec.add(cmd1, **args1).get_reply(err_msg)
936 cmd4 = u"ip_neighbor_add_del"
940 sw_if_index=Topology.get_interface_sw_index(
941 nodes[u"DUT1"], if1_key
945 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
947 ip_address=str(ip_address(if2_ip_addr))
950 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
951 papi_exec.add(cmd4, **args4).get_reply(err_msg)
952 # Configure IPsec tunnel interfaces
954 sw_if_index=loop_sw_if_idx,
959 cmd2 = u"ipsec_tunnel_if_add_del"
966 crypto_alg=crypto_alg.alg_int_repr,
967 local_crypto_key_len=0,
968 local_crypto_key=None,
969 remote_crypto_key_len=0,
970 remote_crypto_key=None,
971 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
972 local_integ_key_len=0,
973 local_integ_key=None,
974 remote_integ_key_len=0,
975 remote_integ_key=None,
978 err_msg = f"Failed to add IPsec tunnel interfaces " \
979 f"on host {nodes[u'DUT1'][u'host']}"
980 ipsec_tunnels = list()
983 for i in range(n_tunnels):
985 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
989 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
991 args1[u"prefix"] = IPUtil.create_prefix_object(
992 if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32
994 args2[u"local_spi"] = spi_1 + i
995 args2[u"remote_spi"] = spi_2 + i
996 args2[u"local_ip"] = IPUtil.create_ip_address_object(
997 if1_ip + i * addr_incr
999 args2[u"remote_ip"] = IPUtil.create_ip_address_object(if2_ip)
1000 args2[u"local_crypto_key_len"] = len(ckeys[i])
1001 args2[u"local_crypto_key"] = ckeys[i]
1002 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1003 args2[u"remote_crypto_key"] = ckeys[i]
1005 args2[u"local_integ_key_len"] = len(ikeys[i])
1006 args2[u"local_integ_key"] = ikeys[i]
1007 args2[u"remote_integ_key_len"] = len(ikeys[i])
1008 args2[u"remote_integ_key"] = ikeys[i]
1009 history = bool(not 1 < i < n_tunnels - 2)
1010 papi_exec.add(cmd1, history=history, **args1).\
1011 add(cmd2, history=history, **args2)
1012 replies = papi_exec.get_replies(err_msg)
1013 for reply in replies:
1014 if u"sw_if_index" in reply:
1015 ipsec_tunnels.append(reply[u"sw_if_index"])
1016 # Configure IP routes
1017 cmd1 = u"sw_interface_set_unnumbered"
1020 sw_if_index=InterfaceUtil.get_interface_index(
1021 nodes[u"DUT1"], if1_key
1023 unnumbered_sw_if_index=0
1025 cmd2 = u"sw_interface_set_flags"
1028 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1030 cmd3 = u"ip_route_add_del"
1036 err_msg = f"Failed to add IP routes " \
1037 f"on host {nodes[u'DUT1'][u'host']}"
1038 for i in range(n_tunnels):
1039 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1040 args2[u"sw_if_index"] = ipsec_tunnels[i]
1041 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1042 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1043 prefix_len=128 if raddr_ip2.version == 6 else 32,
1044 interface=ipsec_tunnels[i]
1046 history = bool(not 1 < i < n_tunnels - 2)
1047 papi_exec.add(cmd1, history=history, **args1).\
1048 add(cmd2, history=history, **args2).\
1049 add(cmd3, history=history, **args3)
1050 papi_exec.get_replies(err_msg)
1052 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1053 # Set IP address on VPP node 2 interface
1054 cmd1 = u"sw_interface_add_del_address"
1056 sw_if_index=InterfaceUtil.get_interface_index(
1057 nodes[u"DUT2"], if2_key
1061 prefix=IPUtil.create_prefix_object(
1062 if2_ip, 96 if if2_ip.version == 6 else 24
1065 err_msg = f"Failed to set IP address on interface {if2_key} " \
1066 f"on host {nodes[u'DUT2'][u'host']}"
1067 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1068 # Configure IPsec tunnel interfaces
1069 cmd2 = u"ipsec_tunnel_if_add_del"
1072 local_ip=IPUtil.create_ip_address_object(if2_ip),
1076 crypto_alg=crypto_alg.alg_int_repr,
1077 local_crypto_key_len=0,
1078 local_crypto_key=None,
1079 remote_crypto_key_len=0,
1080 remote_crypto_key=None,
1081 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1082 local_integ_key_len=0,
1083 local_integ_key=None,
1084 remote_integ_key_len=0,
1085 remote_integ_key=None,
1088 err_msg = f"Failed to add IPsec tunnel interfaces " \
1089 f"on host {nodes[u'DUT2'][u'host']}"
1090 ipsec_tunnels = list()
1091 for i in range(n_tunnels):
1092 args2[u"local_spi"] = spi_2 + i
1093 args2[u"remote_spi"] = spi_1 + i
1094 args2[u"local_ip"] = IPUtil.create_ip_address_object(if2_ip)
1095 args2[u"remote_ip"] = IPUtil.create_ip_address_object(
1096 if1_ip + i * addr_incr)
1097 args2[u"local_crypto_key_len"] = len(ckeys[i])
1098 args2[u"local_crypto_key"] = ckeys[i]
1099 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1100 args2[u"remote_crypto_key"] = ckeys[i]
1102 args2[u"local_integ_key_len"] = len(ikeys[i])
1103 args2[u"local_integ_key"] = ikeys[i]
1104 args2[u"remote_integ_key_len"] = len(ikeys[i])
1105 args2[u"remote_integ_key"] = ikeys[i]
1106 history = bool(not 1 < i < n_tunnels - 2)
1107 papi_exec.add(cmd2, history=history, **args2)
1108 replies = papi_exec.get_replies(err_msg)
1109 for reply in replies:
1110 if u"sw_if_index" in reply:
1111 ipsec_tunnels.append(reply[u"sw_if_index"])
1112 # Configure IP routes
1113 cmd1 = u"ip_route_add_del"
1114 route = IPUtil.compose_vpp_route_structure(
1115 nodes[u"DUT2"], if1_ip.compressed,
1116 prefix_len=32 if if1_ip.version == 6 else 8,
1118 gateway=(if2_ip - 1).compressed
1125 papi_exec.add(cmd1, **args1)
1126 cmd1 = u"sw_interface_set_unnumbered"
1129 sw_if_index=InterfaceUtil.get_interface_index(
1130 nodes[u"DUT2"], if2_key
1132 unnumbered_sw_if_index=0
1134 cmd2 = u"sw_interface_set_flags"
1137 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1139 cmd3 = u"ip_route_add_del"
1145 err_msg = f"Failed to add IP routes " \
1146 f"on host {nodes[u'DUT2'][u'host']}"
1147 for i in range(n_tunnels):
1148 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1149 args2[u"sw_if_index"] = ipsec_tunnels[i]
1150 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1151 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1152 prefix_len=128 if raddr_ip1.version == 6 else 32,
1153 interface=ipsec_tunnels[i]
1155 history = bool(not 1 < i < n_tunnels - 2)
1156 papi_exec.add(cmd1, history=history, **args1). \
1157 add(cmd2, history=history, **args2). \
1158 add(cmd3, history=history, **args3)
1159 papi_exec.get_replies(err_msg)
1162 def vpp_ipsec_add_multiple_tunnels(
1163 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1164 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1165 """Create multiple IPsec tunnels between two VPP nodes.
1167 :param nodes: VPP nodes to create tunnels.
1168 :param interface1: Interface name or sw_if_index on node 1.
1169 :param interface2: Interface name or sw_if_index on node 2.
1170 :param n_tunnels: Number of tunnels to create.
1171 :param crypto_alg: The encryption algorithm name.
1172 :param integ_alg: The integrity algorithm name.
1173 :param tunnel_ip1: Tunnel node1 IPv4 address.
1174 :param tunnel_ip2: Tunnel node2 IPv4 address.
1175 :param raddr_ip1: Policy selector remote IPv4 start address for the
1176 first tunnel in direction node1->node2.
1177 :param raddr_ip2: Policy selector remote IPv4 start address for the
1178 first tunnel in direction node2->node1.
1179 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1180 addresses. Valid values are from 1 to 32.
1182 :type interface1: str or int
1183 :type interface2: str or int
1184 :type n_tunnels: int
1185 :type crypto_alg: CryptoAlg
1186 :type integ_alg: IntegAlg
1187 :type tunnel_ip1: str
1188 :type tunnel_ip2: str
1189 :type raddr_ip1: string
1190 :type raddr_ip2: string
1191 :type raddr_range: int
1201 crypto_key = gen_key(
1202 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1204 integ_key = gen_key(
1205 IPsecUtil.get_integ_alg_key_len(integ_alg)
1206 ).decode() if integ_alg else u""
1208 IPsecUtil.vpp_ipsec_set_ip_route(
1209 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1210 interface1, raddr_range)
1211 IPsecUtil.vpp_ipsec_set_ip_route(
1212 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1213 interface2, raddr_range)
1215 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1216 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1217 IPsecUtil.vpp_ipsec_policy_add(
1218 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1219 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1221 IPsecUtil.vpp_ipsec_policy_add(
1222 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1223 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1226 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1227 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1228 IPsecUtil.vpp_ipsec_policy_add(
1229 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1230 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1232 IPsecUtil.vpp_ipsec_policy_add(
1233 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1234 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1237 IPsecUtil.vpp_ipsec_add_sad_entries(
1238 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1239 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1241 IPsecUtil.vpp_ipsec_spd_add_entries(
1242 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1245 IPsecUtil.vpp_ipsec_add_sad_entries(
1246 nodes[u"DUT2"], 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[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1253 IPsecUtil.vpp_ipsec_add_sad_entries(
1254 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1255 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1258 IPsecUtil.vpp_ipsec_spd_add_entries(
1259 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1262 IPsecUtil.vpp_ipsec_add_sad_entries(
1263 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1264 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1267 IPsecUtil.vpp_ipsec_spd_add_entries(
1268 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1272 def vpp_ipsec_show(node):
1273 """Run "show ipsec" debug CLI command.
1275 :param node: Node to run command on.
1278 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")