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} "
851 f"ipsec_tunnel_if_add_del "
852 f"local_spi {spi_2 + i} remote_spi {spi_1 + i} "
853 f"crypto_alg {crypto_alg.alg_name} "
854 f"local_crypto_key {ckey} remote_crypto_key {ckey} "
856 f"local_ip {if2_ip} "
857 f"remote_ip {if1_ip + i * addr_incr} "
861 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
862 copy_on_execute=True,
863 history=bool(n_tunnels < 100)
866 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
867 copy_on_execute=True,
868 history=bool(n_tunnels < 100)
873 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
874 raddr = ip_network(if1_ip_addr + u"/8", False)
876 f"exec ip route add {raddr} via {if2_n} {if2_ip - 1}\n"
878 for i in range(n_tunnels):
880 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
881 f"exec set interface state ipip{i} up\n"
882 f"exec ip route add {raddr_ip2 + i}/{mask2} "
886 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
887 f"exec set interface state ipip{i} up\n"
888 f"exec ip route add {raddr_ip1 + i}/{mask2} "
892 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
893 copy_on_execute=True,
894 history=bool(n_tunnels < 100)
897 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
898 copy_on_execute=True,
899 history=bool(n_tunnels < 100)
905 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
906 # Create loopback interface on DUT1, set it to up state
907 cmd1 = u"create_loopback"
911 err_msg = f"Failed to create loopback interface " \
912 f"on host {nodes[u'DUT1'][u'host']}"
913 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
914 get_sw_if_index(err_msg)
915 cmd1 = u"sw_interface_set_flags"
917 sw_if_index=loop_sw_if_idx,
918 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
920 err_msg = f"Failed to set loopback interface state up " \
921 f"on host {nodes[u'DUT1'][u'host']}"
922 papi_exec.add(cmd1, **args1).get_reply(err_msg)
923 # Set IP address on VPP node 1 interface
924 cmd1 = u"sw_interface_add_del_address"
926 sw_if_index=InterfaceUtil.get_interface_index(
927 nodes[u"DUT1"], if1_key
931 prefix=IPUtil.create_prefix_object(
932 if2_ip - 1, 96 if if2_ip.version == 6 else 24
935 err_msg = f"Failed to set IP address on interface {if1_key} " \
936 f"on host {nodes[u'DUT1'][u'host']}"
937 papi_exec.add(cmd1, **args1).get_reply(err_msg)
938 cmd4 = u"ip_neighbor_add_del"
942 sw_if_index=Topology.get_interface_sw_index(
943 nodes[u"DUT1"], if1_key
947 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
949 ip_address=str(ip_address(if2_ip_addr))
952 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
953 papi_exec.add(cmd4, **args4).get_reply(err_msg)
954 # Configure IPsec tunnel interfaces
956 sw_if_index=loop_sw_if_idx,
961 cmd2 = u"ipsec_tunnel_if_add_del"
968 crypto_alg=crypto_alg.alg_int_repr,
969 local_crypto_key_len=0,
970 local_crypto_key=None,
971 remote_crypto_key_len=0,
972 remote_crypto_key=None,
973 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
974 local_integ_key_len=0,
975 local_integ_key=None,
976 remote_integ_key_len=0,
977 remote_integ_key=None,
980 err_msg = f"Failed to add IPsec tunnel interfaces " \
981 f"on host {nodes[u'DUT1'][u'host']}"
982 ipsec_tunnels = list()
985 for i in range(n_tunnels):
987 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
991 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
993 args1[u"prefix"] = IPUtil.create_prefix_object(
994 if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32
996 args2[u"local_spi"] = spi_1 + i
997 args2[u"remote_spi"] = spi_2 + i
998 args2[u"local_ip"] = IPUtil.create_ip_address_object(
999 if1_ip + i * addr_incr
1001 args2[u"remote_ip"] = IPUtil.create_ip_address_object(if2_ip)
1002 args2[u"local_crypto_key_len"] = len(ckeys[i])
1003 args2[u"local_crypto_key"] = ckeys[i]
1004 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1005 args2[u"remote_crypto_key"] = ckeys[i]
1007 args2[u"local_integ_key_len"] = len(ikeys[i])
1008 args2[u"local_integ_key"] = ikeys[i]
1009 args2[u"remote_integ_key_len"] = len(ikeys[i])
1010 args2[u"remote_integ_key"] = ikeys[i]
1011 history = bool(not 1 < i < n_tunnels - 2)
1012 papi_exec.add(cmd1, history=history, **args1).\
1013 add(cmd2, history=history, **args2)
1014 replies = papi_exec.get_replies(err_msg)
1015 for reply in replies:
1016 if u"sw_if_index" in reply:
1017 ipsec_tunnels.append(reply[u"sw_if_index"])
1018 # Configure IP routes
1019 cmd1 = u"sw_interface_set_unnumbered"
1022 sw_if_index=InterfaceUtil.get_interface_index(
1023 nodes[u"DUT1"], if1_key
1025 unnumbered_sw_if_index=0
1027 cmd2 = u"sw_interface_set_flags"
1030 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1032 cmd3 = u"ip_route_add_del"
1038 err_msg = f"Failed to add IP routes " \
1039 f"on host {nodes[u'DUT1'][u'host']}"
1040 for i in range(n_tunnels):
1041 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1042 args2[u"sw_if_index"] = ipsec_tunnels[i]
1043 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1044 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1045 prefix_len=128 if raddr_ip2.version == 6 else 32,
1046 interface=ipsec_tunnels[i]
1048 history = bool(not 1 < i < n_tunnels - 2)
1049 papi_exec.add(cmd1, history=history, **args1).\
1050 add(cmd2, history=history, **args2).\
1051 add(cmd3, history=history, **args3)
1052 papi_exec.get_replies(err_msg)
1054 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1055 # Set IP address on VPP node 2 interface
1056 cmd1 = u"sw_interface_add_del_address"
1058 sw_if_index=InterfaceUtil.get_interface_index(
1059 nodes[u"DUT2"], if2_key
1063 prefix=IPUtil.create_prefix_object(
1064 if2_ip, 96 if if2_ip.version == 6 else 24
1067 err_msg = f"Failed to set IP address on interface {if2_key} " \
1068 f"on host {nodes[u'DUT2'][u'host']}"
1069 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1070 # Configure IPsec tunnel interfaces
1071 cmd2 = u"ipsec_tunnel_if_add_del"
1074 local_ip=IPUtil.create_ip_address_object(if2_ip),
1078 crypto_alg=crypto_alg.alg_int_repr,
1079 local_crypto_key_len=0,
1080 local_crypto_key=None,
1081 remote_crypto_key_len=0,
1082 remote_crypto_key=None,
1083 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1084 local_integ_key_len=0,
1085 local_integ_key=None,
1086 remote_integ_key_len=0,
1087 remote_integ_key=None,
1090 err_msg = f"Failed to add IPsec tunnel interfaces " \
1091 f"on host {nodes[u'DUT2'][u'host']}"
1092 ipsec_tunnels = list()
1093 for i in range(n_tunnels):
1094 args2[u"local_spi"] = spi_2 + i
1095 args2[u"remote_spi"] = spi_1 + i
1096 args2[u"local_ip"] = IPUtil.create_ip_address_object(if2_ip)
1097 args2[u"remote_ip"] = IPUtil.create_ip_address_object(
1098 if1_ip + i * addr_incr)
1099 args2[u"local_crypto_key_len"] = len(ckeys[i])
1100 args2[u"local_crypto_key"] = ckeys[i]
1101 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1102 args2[u"remote_crypto_key"] = ckeys[i]
1104 args2[u"local_integ_key_len"] = len(ikeys[i])
1105 args2[u"local_integ_key"] = ikeys[i]
1106 args2[u"remote_integ_key_len"] = len(ikeys[i])
1107 args2[u"remote_integ_key"] = ikeys[i]
1108 history = bool(not 1 < i < n_tunnels - 2)
1109 papi_exec.add(cmd2, history=history, **args2)
1110 replies = papi_exec.get_replies(err_msg)
1111 for reply in replies:
1112 if u"sw_if_index" in reply:
1113 ipsec_tunnels.append(reply[u"sw_if_index"])
1114 # Configure IP routes
1115 cmd1 = u"ip_route_add_del"
1116 route = IPUtil.compose_vpp_route_structure(
1117 nodes[u"DUT2"], if1_ip.compressed,
1118 prefix_len=32 if if1_ip.version == 6 else 8,
1120 gateway=(if2_ip - 1).compressed
1127 papi_exec.add(cmd1, **args1)
1128 cmd1 = u"sw_interface_set_unnumbered"
1131 sw_if_index=InterfaceUtil.get_interface_index(
1132 nodes[u"DUT2"], if2_key
1134 unnumbered_sw_if_index=0
1136 cmd2 = u"sw_interface_set_flags"
1139 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1141 cmd3 = u"ip_route_add_del"
1147 err_msg = f"Failed to add IP routes " \
1148 f"on host {nodes[u'DUT2'][u'host']}"
1149 for i in range(n_tunnels):
1150 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1151 args2[u"sw_if_index"] = ipsec_tunnels[i]
1152 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1153 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1154 prefix_len=128 if raddr_ip1.version == 6 else 32,
1155 interface=ipsec_tunnels[i]
1157 history = bool(not 1 < i < n_tunnels - 2)
1158 papi_exec.add(cmd1, history=history, **args1). \
1159 add(cmd2, history=history, **args2). \
1160 add(cmd3, history=history, **args3)
1161 papi_exec.get_replies(err_msg)
1164 def vpp_ipsec_add_multiple_tunnels(
1165 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1166 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1167 """Create multiple IPsec tunnels between two VPP nodes.
1169 :param nodes: VPP nodes to create tunnels.
1170 :param interface1: Interface name or sw_if_index on node 1.
1171 :param interface2: Interface name or sw_if_index on node 2.
1172 :param n_tunnels: Number of tunnels to create.
1173 :param crypto_alg: The encryption algorithm name.
1174 :param integ_alg: The integrity algorithm name.
1175 :param tunnel_ip1: Tunnel node1 IPv4 address.
1176 :param tunnel_ip2: Tunnel node2 IPv4 address.
1177 :param raddr_ip1: Policy selector remote IPv4 start address for the
1178 first tunnel in direction node1->node2.
1179 :param raddr_ip2: Policy selector remote IPv4 start address for the
1180 first tunnel in direction node2->node1.
1181 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1182 addresses. Valid values are from 1 to 32.
1184 :type interface1: str or int
1185 :type interface2: str or int
1186 :type n_tunnels: int
1187 :type crypto_alg: CryptoAlg
1188 :type integ_alg: IntegAlg
1189 :type tunnel_ip1: str
1190 :type tunnel_ip2: str
1191 :type raddr_ip1: string
1192 :type raddr_ip2: string
1193 :type raddr_range: int
1203 crypto_key = gen_key(
1204 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1206 integ_key = gen_key(
1207 IPsecUtil.get_integ_alg_key_len(integ_alg)
1208 ).decode() if integ_alg else u""
1210 IPsecUtil.vpp_ipsec_set_ip_route(
1211 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1212 interface1, raddr_range)
1213 IPsecUtil.vpp_ipsec_set_ip_route(
1214 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1215 interface2, raddr_range)
1217 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1218 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1219 IPsecUtil.vpp_ipsec_policy_add(
1220 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1221 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1223 IPsecUtil.vpp_ipsec_policy_add(
1224 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1225 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1228 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1229 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1230 IPsecUtil.vpp_ipsec_policy_add(
1231 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1232 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1234 IPsecUtil.vpp_ipsec_policy_add(
1235 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1236 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1239 IPsecUtil.vpp_ipsec_add_sad_entries(
1240 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1241 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1243 IPsecUtil.vpp_ipsec_spd_add_entries(
1244 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1247 IPsecUtil.vpp_ipsec_add_sad_entries(
1248 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1249 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1251 IPsecUtil.vpp_ipsec_spd_add_entries(
1252 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1255 IPsecUtil.vpp_ipsec_add_sad_entries(
1256 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1257 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1260 IPsecUtil.vpp_ipsec_spd_add_entries(
1261 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1264 IPsecUtil.vpp_ipsec_add_sad_entries(
1265 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1266 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1269 IPsecUtil.vpp_ipsec_spd_add_entries(
1270 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1274 def vpp_ipsec_show(node):
1275 """Run "show ipsec" debug CLI command.
1277 :param node: Node to run command on.
1280 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")