1 # Copyright (c) 2024 Cisco and/or its affiliates.
2 # Copyright (c) 2024 PANTHEON.tech s.r.o.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """IPsec utilities library."""
17 from enum import Enum, IntEnum
19 from ipaddress import ip_network, ip_address
20 from random import choice
21 from string import ascii_letters
23 from robot.libraries.BuiltIn import BuiltIn
25 from resources.libraries.python.Constants import Constants
26 from resources.libraries.python.IncrementUtil import ObjIncrement
27 from resources.libraries.python.InterfaceUtil import (
31 from resources.libraries.python.IPAddress import IPAddress
32 from resources.libraries.python.IPUtil import (
38 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
39 from resources.libraries.python.ssh import scp_node
40 from resources.libraries.python.topology import Topology, NodeType
41 from resources.libraries.python.VPPUtil import VPPUtil
42 from resources.libraries.python.FlowUtil import FlowUtil
45 IPSEC_UDP_PORT_DEFAULT = 4500
46 IPSEC_REPLAY_WINDOW_DEFAULT = 64
50 """Generate random string as a key.
52 :param length: Length of generated payload.
54 :returns: The generated payload.
57 return "".join(choice(ascii_letters) for _ in range(length)).encode(
62 class PolicyAction(Enum):
65 BYPASS = ("bypass", 0)
66 DISCARD = ("discard", 1)
67 PROTECT = ("protect", 3)
69 def __init__(self, policy_name, policy_int_repr):
70 self.policy_name = policy_name
71 self.policy_int_repr = policy_int_repr
74 return self.policy_name
77 return self.policy_int_repr
80 class CryptoAlg(Enum):
81 """Encryption algorithms."""
83 AES_CBC_128 = ("aes-cbc-128", 1, "AES-CBC", 16)
84 AES_CBC_256 = ("aes-cbc-256", 3, "AES-CBC", 32)
85 AES_GCM_128 = ("aes-gcm-128", 7, "AES-GCM", 16)
86 AES_GCM_256 = ("aes-gcm-256", 9, "AES-GCM", 32)
88 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
89 self.alg_name = alg_name
90 self.alg_int_repr = alg_int_repr
91 self.scapy_name = scapy_name
92 self.key_len = key_len
96 """Integrity algorithm."""
98 SHA_256_128 = ("sha-256-128", 4, "SHA2-256-128", 32)
99 SHA_512_256 = ("sha-512-256", 6, "SHA2-512-256", 64)
101 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
102 self.alg_name = alg_name
103 self.alg_int_repr = alg_int_repr
104 self.scapy_name = scapy_name
105 self.key_len = key_len
108 class IPsecProto(IntEnum):
109 """IPsec protocol."""
111 IPSEC_API_PROTO_ESP = 50
112 IPSEC_API_PROTO_AH = 51
115 class IPsecSadFlags(IntEnum):
116 """IPsec Security Association Database flags."""
118 IPSEC_API_SAD_FLAG_NONE = 0
119 # Enable extended sequence numbers
120 IPSEC_API_SAD_FLAG_USE_ESN = 0x01
121 # Enable Anti - replay
122 IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02
123 # IPsec tunnel mode if non-zero, else transport mode
124 IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04
125 # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
126 # only valid if is_tunnel is non-zero
127 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08
128 # Enable UDP encapsulation for NAT traversal
129 IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10
130 # IPsec SA is or inbound traffic
131 IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
134 class TunnelEncpaDecapFlags(IntEnum):
135 """Flags controlling tunnel behaviour."""
137 TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
138 # at encap, copy the DF bit of the payload into the tunnel header
139 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
140 # at encap, set the DF bit in the tunnel header
141 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
142 # at encap, copy the DSCP bits of the payload into the tunnel header
143 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
144 # at encap, copy the ECN bit of the payload into the tunnel header
145 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
146 # at decap, copy the ECN bit of the tunnel header into the payload
147 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
150 class TunnelMode(IntEnum):
154 TUNNEL_API_MODE_P2P = 0
156 TUNNEL_API_MODE_MP = 1
160 """IPsec utilities."""
163 def policy_action_bypass():
164 """Return policy action bypass.
166 :returns: PolicyAction enum BYPASS object.
169 return PolicyAction.BYPASS
172 def policy_action_discard():
173 """Return policy action discard.
175 :returns: PolicyAction enum DISCARD object.
178 return PolicyAction.DISCARD
181 def policy_action_protect():
182 """Return policy action protect.
184 :returns: PolicyAction enum PROTECT object.
187 return PolicyAction.PROTECT
190 def crypto_alg_aes_cbc_128():
191 """Return encryption algorithm aes-cbc-128.
193 :returns: CryptoAlg enum AES_CBC_128 object.
196 return CryptoAlg.AES_CBC_128
199 def crypto_alg_aes_cbc_256():
200 """Return encryption algorithm aes-cbc-256.
202 :returns: CryptoAlg enum AES_CBC_256 object.
205 return CryptoAlg.AES_CBC_256
208 def crypto_alg_aes_gcm_128():
209 """Return encryption algorithm aes-gcm-128.
211 :returns: CryptoAlg enum AES_GCM_128 object.
214 return CryptoAlg.AES_GCM_128
217 def crypto_alg_aes_gcm_256():
218 """Return encryption algorithm aes-gcm-256.
220 :returns: CryptoAlg enum AES_GCM_128 object.
223 return CryptoAlg.AES_GCM_256
226 def get_crypto_alg_key_len(crypto_alg):
227 """Return encryption algorithm key length.
229 :param crypto_alg: Encryption algorithm.
230 :type crypto_alg: CryptoAlg
231 :returns: Key length.
234 return crypto_alg.key_len
237 def get_crypto_alg_scapy_name(crypto_alg):
238 """Return encryption algorithm scapy name.
240 :param crypto_alg: Encryption algorithm.
241 :type crypto_alg: CryptoAlg
242 :returns: Algorithm scapy name.
245 return crypto_alg.scapy_name
248 def integ_alg_sha_256_128():
249 """Return integrity algorithm SHA-256-128.
251 :returns: IntegAlg enum SHA_256_128 object.
254 return IntegAlg.SHA_256_128
257 def integ_alg_sha_512_256():
258 """Return integrity algorithm SHA-512-256.
260 :returns: IntegAlg enum SHA_512_256 object.
263 return IntegAlg.SHA_512_256
266 def get_integ_alg_key_len(integ_alg):
267 """Return integrity algorithm key length.
269 None argument is accepted, returning zero.
271 :param integ_alg: Integrity algorithm.
272 :type integ_alg: Optional[IntegAlg]
273 :returns: Key length.
276 return 0 if integ_alg is None else integ_alg.key_len
279 def get_integ_alg_scapy_name(integ_alg):
280 """Return integrity algorithm scapy name.
282 :param integ_alg: Integrity algorithm.
283 :type integ_alg: IntegAlg
284 :returns: Algorithm scapy name.
287 return integ_alg.scapy_name
290 def ipsec_proto_esp():
291 """Return IPSec protocol ESP.
293 :returns: IPsecProto enum ESP object.
296 return int(IPsecProto.IPSEC_API_PROTO_ESP)
299 def ipsec_proto_ah():
300 """Return IPSec protocol AH.
302 :returns: IPsecProto enum AH object.
305 return int(IPsecProto.IPSEC_API_PROTO_AH)
308 def vpp_ipsec_select_backend(node, protocol, index=1):
309 """Select IPsec backend.
311 :param node: VPP node to select IPsec backend on.
312 :param protocol: IPsec protocol.
313 :param index: Backend index.
315 :type protocol: IPsecProto
317 :raises RuntimeError: If failed to select IPsec backend or if no API
320 cmd = "ipsec_select_backend"
321 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
322 args = dict(protocol=protocol, index=index)
323 with PapiSocketExecutor(node) as papi_exec:
324 papi_exec.add(cmd, **args).get_reply(err_msg)
327 def vpp_ipsec_set_async_mode(node, async_enable=1):
328 """Set IPsec async mode on|off.
330 Unconditionally, attempt to switch crypto dispatch into polling mode.
332 :param node: VPP node to set IPsec async mode.
333 :param async_enable: Async mode on or off.
335 :type async_enable: int
336 :raises RuntimeError: If failed to set IPsec async mode or if no API
339 with PapiSocketExecutor(node) as papi_exec:
340 cmd = "ipsec_set_async_mode"
341 err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
342 args = dict(async_enable=async_enable)
343 papi_exec.add(cmd, **args).get_reply(err_msg)
344 cmd = "crypto_set_async_dispatch_v2"
345 err_msg = "Failed to set dispatch mode."
346 args = dict(mode=0, adaptive=False)
348 papi_exec.add(cmd, **args).get_reply(err_msg)
349 except (AttributeError, RuntimeError):
350 # Expected when VPP build does not have the _v2 yet
351 # (after and before the first CRC check).
352 # TODO: Fail here when testing of pre-23.10 builds is over.
356 def vpp_ipsec_crypto_sw_scheduler_set_worker(
357 node, workers, crypto_enable=False
359 """Enable or disable crypto on specific vpp worker threads.
361 :param node: VPP node to enable or disable crypto for worker threads.
362 :param workers: List of VPP thread numbers.
363 :param crypto_enable: Disable or enable crypto work.
365 :type workers: Iterable[int]
366 :type crypto_enable: bool
367 :raises RuntimeError: If failed to enable or disable crypto for worker
368 thread or if no API reply received.
370 for worker in workers:
371 cmd = "crypto_sw_scheduler_set_worker"
373 f"Failed to disable/enable crypto for worker thread "
374 f"on host {node[u'host']}"
376 args = dict(worker_index=worker - 1, crypto_enable=crypto_enable)
377 with PapiSocketExecutor(node) as papi_exec:
378 papi_exec.add(cmd, **args).get_reply(err_msg)
381 def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
382 nodes, crypto_enable=False
384 """Enable or disable crypto on specific vpp worker threads.
386 :param node: VPP node to enable or disable crypto for worker threads.
387 :param crypto_enable: Disable or enable crypto work.
389 :type crypto_enable: bool
390 :raises RuntimeError: If failed to enable or disable crypto for worker
391 thread or if no API reply received.
393 for node_name, node in nodes.items():
394 if node["type"] == NodeType.DUT:
395 thread_data = VPPUtil.vpp_show_threads(node)
396 worker_cnt = len(thread_data) - 1
400 workers = BuiltIn().get_variable_value(
401 f"${{{node_name}_cpu_dp}}"
403 for item in thread_data:
404 if str(item.cpu_id) in workers.split(","):
405 worker_ids.append(item.id)
407 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
408 node, workers=worker_ids, crypto_enable=crypto_enable
412 def vpp_ipsec_add_sad_entry(
423 """Create Security Association Database entry on the VPP node.
425 :param node: VPP node to add SAD entry on.
426 :param sad_id: SAD entry ID.
427 :param spi: Security Parameter Index of this SAD entry.
428 :param crypto_alg: The encryption algorithm name.
429 :param crypto_key: The encryption key string.
430 :param integ_alg: The integrity algorithm name.
431 :param integ_key: The integrity key string.
432 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
433 specified ESP transport mode is used.
434 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
435 not specified ESP transport mode is used.
439 :type crypto_alg: CryptoAlg
440 :type crypto_key: str
441 :type integ_alg: Optional[IntegAlg]
443 :type tunnel_src: str
444 :type tunnel_dst: str
446 if isinstance(crypto_key, str):
447 crypto_key = crypto_key.encode(encoding="utf-8")
448 if isinstance(integ_key, str):
449 integ_key = integ_key.encode(encoding="utf-8")
450 ckey = dict(length=len(crypto_key), data=crypto_key)
451 ikey = dict(length=len(integ_key), data=integ_key if integ_key else 0)
453 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
454 if tunnel_src and tunnel_dst:
455 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
456 src_addr = ip_address(tunnel_src)
457 dst_addr = ip_address(tunnel_dst)
458 if src_addr.version == 6:
460 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
466 cmd = "ipsec_sad_entry_add_v2"
468 f"Failed to add Security Association Database entry "
469 f"on host {node[u'host']}"
474 crypto_algorithm=crypto_alg.alg_int_repr,
476 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
483 encap_decap_flags=int(
484 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
486 dscp=int(IpDscp.IP_API_DSCP_CS0),
488 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
489 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
490 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
491 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
493 args = dict(entry=sad_entry)
494 with PapiSocketExecutor(node) as papi_exec:
495 papi_exec.add(cmd, **args).get_reply(err_msg)
498 def vpp_ipsec_add_sad_entries(
509 tunnel_addr_incr=True,
511 """Create multiple Security Association Database entries on VPP node.
513 :param node: VPP node to add SAD entry on.
514 :param n_entries: Number of SAD entries to be created.
515 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
517 :param spi: Security Parameter Index of first SAD entry. All subsequent
518 SAD entries will have spi incremented by 1.
519 :param crypto_alg: The encryption algorithm name.
520 :param crypto_key: The encryption key string.
521 :param integ_alg: The integrity algorithm name.
522 :param integ_key: The integrity key string.
523 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
524 specified ESP transport mode is used.
525 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
526 not specified ESP transport mode is used.
527 :param tunnel_addr_incr: Enable or disable tunnel IP address
533 :type crypto_alg: CryptoAlg
534 :type crypto_key: str
535 :type integ_alg: Optional[IntegAlg]
537 :type tunnel_src: str
538 :type tunnel_dst: str
539 :type tunnel_addr_incr: bool
541 if isinstance(crypto_key, str):
542 crypto_key = crypto_key.encode(encoding="utf-8")
543 if isinstance(integ_key, str):
544 integ_key = integ_key.encode(encoding="utf-8")
545 if tunnel_src and tunnel_dst:
546 src_addr = ip_address(tunnel_src)
547 dst_addr = ip_address(tunnel_dst)
554 1 << (128 - 96) if src_addr.version == 6 else 1 << (32 - 24)
559 ckey = dict(length=len(crypto_key), data=crypto_key)
560 ikey = dict(length=len(integ_key), data=integ_key if integ_key else 0)
562 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
563 if tunnel_src and tunnel_dst:
564 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
565 if src_addr.version == 6:
567 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
570 cmd = "ipsec_sad_entry_add_v2"
572 f"Failed to add Security Association Database entry "
573 f"on host {node[u'host']}"
579 crypto_algorithm=crypto_alg.alg_int_repr,
581 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
588 encap_decap_flags=int(
589 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
591 dscp=int(IpDscp.IP_API_DSCP_CS0),
593 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
594 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
595 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
596 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
598 args = dict(entry=sad_entry)
599 with PapiSocketExecutor(node, is_async=True) as papi_exec:
600 for i in range(n_entries):
601 args["entry"]["sad_id"] = int(sad_id) + i
602 args["entry"]["spi"] = int(spi) + i
603 args["entry"]["tunnel"]["src"] = (
604 str(src_addr + i * addr_incr)
605 if tunnel_src and tunnel_dst
608 args["entry"]["tunnel"]["dst"] = (
609 str(dst_addr + i * addr_incr)
610 if tunnel_src and tunnel_dst
613 history = bool(not 1 < i < n_entries - 2)
614 papi_exec.add(cmd, history=history, **args)
615 papi_exec.get_replies(err_msg)
618 def vpp_ipsec_set_ip_route(
628 """Set IP address and route on interface.
630 :param node: VPP node to add config on.
631 :param n_tunnels: Number of tunnels to create.
632 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
633 :param traffic_addr: Traffic destination IP address to route.
634 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
635 :param interface: Interface key on node 1.
636 :param raddr_range: Mask specifying range of Policy selector Remote IP
637 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
639 :param dst_mac: The MAC address of destination tunnels.
642 :type tunnel_src: str
643 :type traffic_addr: str
644 :type tunnel_dst: str
646 :type raddr_range: int
649 tunnel_src = ip_address(tunnel_src)
650 tunnel_dst = ip_address(tunnel_dst)
651 traffic_addr = ip_address(traffic_addr)
652 tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
654 1 << (128 - raddr_range)
655 if tunnel_src.version == 6
656 else 1 << (32 - raddr_range)
659 cmd1 = "sw_interface_add_del_address"
661 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
666 cmd2 = "ip_route_add_del"
667 args2 = dict(is_add=1, is_multipath=0, route=None)
668 cmd3 = "ip_neighbor_add_del"
672 sw_if_index=Topology.get_interface_sw_index(node, interface),
674 mac_address=str(dst_mac),
679 f"Failed to configure IP addresses, IP routes and "
680 f"IP neighbor on interface {interface} on host {node[u'host']}"
682 else f"Failed to configure IP addresses and IP routes "
683 f"on interface {interface} on host {node[u'host']}"
686 with PapiSocketExecutor(node, is_async=True) as papi_exec:
687 for i in range(n_tunnels):
688 tunnel_dst_addr = tunnel_dst + i * addr_incr
689 args1["prefix"] = IPUtil.create_prefix_object(
690 tunnel_src + i * addr_incr, raddr_range
692 args2["route"] = IPUtil.compose_vpp_route_structure(
695 prefix_len=tunnel_dst_prefix,
697 gateway=tunnel_dst_addr,
699 history = bool(not 1 < i < n_tunnels - 2)
700 papi_exec.add(cmd1, history=history, **args1)
701 papi_exec.add(cmd2, history=history, **args2)
703 args2["route"] = IPUtil.compose_vpp_route_structure(
706 prefix_len=tunnel_dst_prefix,
708 gateway=tunnel_dst_addr,
710 papi_exec.add(cmd2, history=history, **args2)
713 args3["neighbor"]["ip_address"] = ip_address(
716 papi_exec.add(cmd3, history=history, **args3)
717 papi_exec.get_replies(err_msg)
720 def vpp_ipsec_add_spd(node, spd_id):
721 """Create Security Policy Database on the VPP node.
723 :param node: VPP node to add SPD on.
724 :param spd_id: SPD ID.
728 cmd = "ipsec_spd_add_del"
730 f"Failed to add Security Policy Database "
731 f"on host {node[u'host']}"
733 args = dict(is_add=True, spd_id=int(spd_id))
734 with PapiSocketExecutor(node) as papi_exec:
735 papi_exec.add(cmd, **args).get_reply(err_msg)
738 def vpp_ipsec_spd_add_if(node, spd_id, interface):
739 """Add interface to the Security Policy Database.
741 :param node: VPP node.
742 :param spd_id: SPD ID to add interface on.
743 :param interface: Interface name or sw_if_index.
746 :type interface: str or int
748 cmd = "ipsec_interface_add_del_spd"
750 f"Failed to add interface {interface} to Security Policy "
751 f"Database {spd_id} on host {node[u'host']}"
755 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
758 with PapiSocketExecutor(node) as papi_exec:
759 papi_exec.add(cmd, **args).get_reply(err_msg)
762 def vpp_ipsec_create_spds_match_nth_entry(
769 action=PolicyAction.BYPASS,
773 """Create one matching SPD entry for inbound or outbound traffic on
774 a DUT for each traffic direction and also create entry_amount - 1
775 non-matching SPD entries. Create a Security Policy Database on each
776 outbound interface where these entries will be configured.
777 The matching SPD entry will have the lowest priority, input action and
778 will be configured to match the IP flow. The non-matching entries will
779 be the same, except with higher priority and non-matching IP flows.
781 Action Protect is currently not supported.
783 :param node: VPP node to configured the SPDs and their entries.
784 :param dir1_interface: The interface in direction 1 where the entries
786 :param dir2_interface: The interface in direction 2 where the entries
788 :param entry_amount: The number of SPD entries to configure. If
789 entry_amount == 1, no non-matching entries will be configured.
790 :param local_addr_range: Matching local address range in direction 1
791 in format IP/prefix or IP/mask. If no mask is provided, it's
792 considered to be /32.
793 :param remote_addr_range: Matching remote address range in
794 direction 1 in format IP/prefix or IP/mask. If no mask is
795 provided, it's considered to be /32.
796 :param action: Policy action.
797 :param inbound: If True policy is for inbound traffic, otherwise
799 :param bidirectional: When True, will create SPDs in both directions
800 of traffic. When False, only in one direction.
802 :type dir1_interface: Union[string, int]
803 :type dir2_interface: Union[string, int]
804 :type entry_amount: int
805 :type local_addr_range:
806 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
807 :type remote_addr_range:
808 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
809 :type action: IPsecUtil.PolicyAction
811 :type bidirectional: bool
812 :raises NotImplementedError: When the action is PolicyAction.PROTECT.
815 if action == PolicyAction.PROTECT:
816 raise NotImplementedError("Policy action PROTECT is not supported.")
820 matching_priority = 1
822 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
823 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
824 # matching entry direction 1
825 IPsecUtil.vpp_ipsec_add_spd_entry(
831 laddr_range=local_addr_range,
832 raddr_range=remote_addr_range,
836 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
837 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
839 # matching entry direction 2, the address ranges are switched
840 IPsecUtil.vpp_ipsec_add_spd_entry(
846 laddr_range=remote_addr_range,
847 raddr_range=local_addr_range,
850 # non-matching entries
851 no_match_entry_amount = entry_amount - 1
852 if no_match_entry_amount > 0:
853 # create a NetworkIncrement representation of the network,
854 # then skip the matching network
855 no_match_local_addr_range = NetworkIncrement(
856 ip_network(local_addr_range)
858 next(no_match_local_addr_range)
860 no_match_remote_addr_range = NetworkIncrement(
861 ip_network(remote_addr_range)
863 next(no_match_remote_addr_range)
865 # non-matching entries direction 1
866 IPsecUtil.vpp_ipsec_add_spd_entries(
868 no_match_entry_amount,
870 ObjIncrement(matching_priority + 1, 1),
873 laddr_range=no_match_local_addr_range,
874 raddr_range=no_match_remote_addr_range,
878 # reset the networks so that we're using a unified config
879 # the address ranges are switched
880 no_match_remote_addr_range = NetworkIncrement(
881 ip_network(local_addr_range)
883 next(no_match_remote_addr_range)
885 no_match_local_addr_range = NetworkIncrement(
886 ip_network(remote_addr_range)
888 next(no_match_local_addr_range)
889 # non-matching entries direction 2
890 IPsecUtil.vpp_ipsec_add_spd_entries(
892 no_match_entry_amount,
894 ObjIncrement(matching_priority + 1, 1),
897 laddr_range=no_match_local_addr_range,
898 raddr_range=no_match_remote_addr_range,
901 IPsecUtil.vpp_ipsec_show_all(node)
904 def _vpp_ipsec_add_spd_entry_internal(
918 """Prepare to create Security Policy Database entry on the VPP node.
920 This just adds one more command to the executor.
921 The call site shall get replies once all entries are added,
922 to get speed benefit from async PAPI.
924 :param executor: Open PAPI executor (async handling) to add commands to.
925 :param spd_id: SPD ID to add entry on.
926 :param priority: SPD entry priority, higher number = higher priority.
927 :param action: Policy action.
928 :param inbound: If True policy is for inbound traffic, otherwise
930 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
931 :param proto: Policy selector next layer protocol number.
932 :param laddr_range: Policy selector local IPv4 or IPv6 address range
933 in format IP/prefix or IP/mask. If no mask is provided,
934 it's considered to be /32.
935 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
936 in format IP/prefix or IP/mask. If no mask is provided,
937 it's considered to be /32.
938 :param lport_range: Policy selector local TCP/UDP port range in format
939 <port_start>-<port_end>.
940 :param rport_range: Policy selector remote TCP/UDP port range in format
941 <port_start>-<port_end>.
942 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
943 not defined so it will default to address ::/0, otherwise False.
944 :type executor: PapiSocketExecutor
947 :type action: IPsecUtil.PolicyAction
951 :type laddr_range: string
952 :type raddr_range: string
953 :type lport_range: string
954 :type rport_range: string
957 if laddr_range is None:
958 laddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
960 if raddr_range is None:
961 raddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
963 local_net = ip_network(laddr_range, strict=False)
964 remote_net = ip_network(raddr_range, strict=False)
966 cmd = "ipsec_spd_entry_add_del_v2"
970 priority=int(priority),
971 is_outbound=not inbound,
972 sa_id=int(sa_id) if sa_id else 0,
974 protocol=255 if proto is None else int(proto),
975 remote_address_start=IPAddress.create_ip_address_object(
976 remote_net.network_address
978 remote_address_stop=IPAddress.create_ip_address_object(
979 remote_net.broadcast_address
981 local_address_start=IPAddress.create_ip_address_object(
982 local_net.network_address
984 local_address_stop=IPAddress.create_ip_address_object(
985 local_net.broadcast_address
988 int(rport_range.split("-")[0]) if rport_range else 0
991 int(rport_range.split("-")[1]) if rport_range else 65535
994 int(lport_range.split("-")[0]) if lport_range else 0
997 int(lport_range.split("-")[1]) if rport_range else 65535
1000 args = dict(is_add=True, entry=spd_entry)
1001 executor.add(cmd, **args)
1004 def vpp_ipsec_add_spd_entry(
1018 """Create Security Policy Database entry on the VPP node.
1020 :param node: VPP node to add SPD entry on.
1021 :param spd_id: SPD ID to add entry on.
1022 :param priority: SPD entry priority, higher number = higher priority.
1023 :param action: Policy action.
1024 :param inbound: If True policy is for inbound traffic, otherwise
1026 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
1027 :param proto: Policy selector next layer protocol number.
1028 :param laddr_range: Policy selector local IPv4 or IPv6 address range
1029 in format IP/prefix or IP/mask. If no mask is provided,
1030 it's considered to be /32.
1031 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
1032 in format IP/prefix or IP/mask. If no mask is provided,
1033 it's considered to be /32.
1034 :param lport_range: Policy selector local TCP/UDP port range in format
1035 <port_start>-<port_end>.
1036 :param rport_range: Policy selector remote TCP/UDP port range in format
1037 <port_start>-<port_end>.
1038 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
1039 not defined so it will default to address ::/0, otherwise False.
1043 :type action: IPsecUtil.PolicyAction
1047 :type laddr_range: string
1048 :type raddr_range: string
1049 :type lport_range: string
1050 :type rport_range: string
1054 f"Failed to add entry to Security Policy Database "
1055 f"{spd_id} on host {node[u'host']}"
1057 with PapiSocketExecutor(node, is_async=True) as papi_exec:
1058 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
1072 papi_exec.get_replies(err_msg)
1075 def vpp_ipsec_add_spd_entries(
1090 """Create multiple Security Policy Database entries on the VPP node.
1092 :param node: VPP node to add SPD entries on.
1093 :param n_entries: Number of SPD entries to be added.
1094 :param spd_id: SPD ID to add entries on.
1095 :param priority: SPD entries priority, higher number = higher priority.
1096 :param action: Policy action.
1097 :param inbound: If True policy is for inbound traffic, otherwise
1099 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
1100 :param proto: Policy selector next layer protocol number.
1101 :param laddr_range: Policy selector local IPv4 or IPv6 address range
1102 in format IP/prefix or IP/mask. If no mask is provided,
1103 it's considered to be /32.
1104 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
1105 in format IP/prefix or IP/mask. If no mask is provided,
1106 it's considered to be /32.
1107 :param lport_range: Policy selector local TCP/UDP port range in format
1108 <port_start>-<port_end>.
1109 :param rport_range: Policy selector remote TCP/UDP port range in format
1110 <port_start>-<port_end>.
1111 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
1112 not defined so it will default to address ::/0, otherwise False.
1114 :type n_entries: int
1116 :type priority: IPsecUtil.ObjIncrement
1117 :type action: IPsecUtil.PolicyAction
1119 :type sa_id: IPsecUtil.ObjIncrement
1121 :type laddr_range: IPsecUtil.NetworkIncrement
1122 :type raddr_range: IPsecUtil.NetworkIncrement
1123 :type lport_range: string
1124 :type rport_range: string
1127 if laddr_range is None:
1128 laddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
1129 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1131 if raddr_range is None:
1132 raddr_range = "::/0" if is_ipv6 else "0.0.0.0/0"
1133 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1135 lport_range_start = 0
1136 lport_range_stop = 65535
1138 lport_range_start, lport_range_stop = lport_range.split("-")
1140 rport_range_start = 0
1141 rport_range_stop = 65535
1143 rport_range_start, rport_range_stop = rport_range.split("-")
1146 f"Failed to add entry to Security Policy Database "
1147 f"{spd_id} on host {node[u'host']}"
1149 with PapiSocketExecutor(node, is_async=True) as papi_exec:
1150 for _ in range(n_entries):
1151 IPsecUtil._vpp_ipsec_add_spd_entry_internal(
1157 next(sa_id) if sa_id is not None else sa_id,
1165 papi_exec.get_replies(err_msg)
1168 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1169 """Create loopback interface and set IP address on VPP node 1 interface
1172 :param nodes: VPP nodes to create tunnel interfaces.
1173 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1174 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1175 IPv4/IPv6 address (ip2).
1176 :param if1_key: VPP node 1 interface key from topology file.
1177 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1178 interface key from topology file.
1184 with PapiSocketExecutor(nodes["DUT1"]) as papi_exec:
1185 # Create loopback interface on DUT1, set it to up state
1186 cmd = "create_loopback_instance"
1193 f"Failed to create loopback interface "
1194 f"on host {nodes[u'DUT1'][u'host']}"
1196 papi_exec.add(cmd, **args)
1197 loop_sw_if_idx = papi_exec.get_sw_if_index(err_msg)
1198 cmd = "sw_interface_set_flags"
1200 sw_if_index=loop_sw_if_idx,
1201 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value,
1204 f"Failed to set loopback interface state up "
1205 f"on host {nodes[u'DUT1'][u'host']}"
1207 papi_exec.add(cmd, **args).get_reply(err_msg)
1208 # Set IP address on VPP node 1 interface
1209 cmd = "sw_interface_add_del_address"
1211 sw_if_index=InterfaceUtil.get_interface_index(
1212 nodes["DUT1"], if1_key
1216 prefix=IPUtil.create_prefix_object(
1218 96 if tun_ips["ip2"].version == 6 else 24,
1222 f"Failed to set IP address on interface {if1_key} "
1223 f"on host {nodes[u'DUT1'][u'host']}"
1225 papi_exec.add(cmd, **args).get_reply(err_msg)
1226 cmd2 = "ip_neighbor_add_del"
1230 sw_if_index=Topology.get_interface_sw_index(
1231 nodes["DUT1"], if1_key
1235 Topology.get_interface_mac(nodes["DUT2"], if2_key)
1236 if "DUT2" in nodes.keys()
1237 else Topology.get_interface_mac(nodes["TG"], if2_key)
1239 ip_address=tun_ips["ip2"].compressed,
1242 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1243 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1245 return loop_sw_if_idx
1248 def _ipsec_create_tunnel_interfaces_dut1_papi(
1261 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1263 Generate random keys and return them (so DUT2 or TG can decrypt).
1265 :param nodes: VPP nodes to create tunnel interfaces.
1266 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1267 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1268 IPv4/IPv6 address (ip2).
1269 :param if1_key: VPP node 1 interface key from topology file.
1270 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1271 interface key from topology file.
1272 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1273 :param crypto_alg: The encryption algorithm name.
1274 :param integ_alg: The integrity algorithm name.
1275 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1276 first tunnel in direction node2->node1.
1277 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1278 :param addr_incr: IP / IPv6 address incremental step.
1279 :param existing_tunnels: Number of tunnel interfaces before creation.
1280 Useful mainly for reconf tests. Default 0.
1285 :type n_tunnels: int
1286 :type crypto_alg: CryptoAlg
1287 :type integ_alg: Optional[IntegAlg]
1288 :type raddr_ip2: IPv4Address or IPv6Address
1289 :type addr_incr: int
1291 :type existing_tunnels: int
1292 :returns: Generated ckeys and ikeys.
1293 :rtype: List[bytes], List[bytes]
1295 if not existing_tunnels:
1296 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1297 nodes, tun_ips, if1_key, if2_key
1300 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1301 nodes["DUT1"], "loop0"
1303 with PapiSocketExecutor(nodes["DUT1"], is_async=True) as papi_exec:
1304 # Configure IP addresses on loop0 interface
1305 cmd = "sw_interface_add_del_address"
1307 sw_if_index=loop_sw_if_idx,
1312 for i in range(existing_tunnels, n_tunnels):
1313 args["prefix"] = IPUtil.create_prefix_object(
1314 tun_ips["ip1"] + i * addr_incr,
1315 128 if tun_ips["ip1"].version == 6 else 32,
1318 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1320 # Configure IPIP tunnel interfaces
1321 cmd = "ipip_add_tunnel"
1323 instance=Constants.BITWISE_NON_ZERO,
1328 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1330 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1331 dscp=int(IpDscp.IP_API_DSCP_CS0),
1333 args = dict(tunnel=ipip_tunnel)
1334 ipip_tunnels = [None] * existing_tunnels
1335 for i in range(existing_tunnels, n_tunnels):
1336 args["tunnel"]["src"] = IPAddress.create_ip_address_object(
1337 tun_ips["ip1"] + i * addr_incr
1339 args["tunnel"]["dst"] = IPAddress.create_ip_address_object(
1343 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1346 f"Failed to add IPIP tunnel interfaces on host"
1347 f" {nodes[u'DUT1'][u'host']}"
1349 ipip_tunnels.extend(
1351 reply["sw_if_index"]
1352 for reply in papi_exec.get_replies(err_msg)
1353 if "sw_if_index" in reply
1356 # Configure IPSec SAD entries
1357 ckeys = [bytes()] * existing_tunnels
1358 ikeys = [bytes()] * existing_tunnels
1359 cmd = "ipsec_sad_entry_add_v2"
1360 c_key = dict(length=0, data=None)
1361 i_key = dict(length=0, data=None)
1365 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1366 crypto_algorithm=crypto_alg.alg_int_repr,
1368 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1369 integrity_key=i_key,
1375 encap_decap_flags=int(
1376 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1378 dscp=int(IpDscp.IP_API_DSCP_CS0),
1381 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
1382 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
1383 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
1385 args = dict(entry=sad_entry)
1386 for i in range(existing_tunnels, n_tunnels):
1388 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1391 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1393 # SAD entry for outband / tx path
1394 args["entry"]["sad_id"] = i
1395 args["entry"]["spi"] = spi_d["spi_1"] + i
1397 args["entry"]["crypto_key"]["length"] = len(ckeys[i])
1398 args["entry"]["crypto_key"]["data"] = ckeys[i]
1400 args["entry"]["integrity_key"]["length"] = len(ikeys[i])
1401 args["entry"]["integrity_key"]["data"] = ikeys[i]
1402 args["entry"]["flags"] = int(
1403 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1406 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1408 # SAD entry for inband / rx path
1409 args["entry"]["sad_id"] = 100000 + i
1410 args["entry"]["spi"] = spi_d["spi_2"] + i
1412 args["entry"]["crypto_key"]["length"] = len(ckeys[i])
1413 args["entry"]["crypto_key"]["data"] = ckeys[i]
1415 args["entry"]["integrity_key"]["length"] = len(ikeys[i])
1416 args["entry"]["integrity_key"]["data"] = ikeys[i]
1417 args["entry"]["flags"] = int(
1418 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1419 | IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1422 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1425 f"Failed to add IPsec SAD entries on host"
1426 f" {nodes[u'DUT1'][u'host']}"
1428 papi_exec.get_replies(err_msg)
1429 # Add protection for tunnels with IPSEC
1430 cmd = "ipsec_tunnel_protect_update"
1433 via_label=MPLS_LABEL_INVALID,
1434 obj_id=Constants.BITWISE_NON_ZERO,
1436 ipsec_tunnel_protect = dict(
1437 sw_if_index=None, nh=n_hop, sa_out=None, n_sa_in=1, sa_in=None
1439 args = dict(tunnel=ipsec_tunnel_protect)
1440 for i in range(existing_tunnels, n_tunnels):
1441 args["tunnel"]["sw_if_index"] = ipip_tunnels[i]
1442 args["tunnel"]["sa_out"] = i
1443 args["tunnel"]["sa_in"] = [100000 + i]
1445 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1448 f"Failed to add protection for tunnels with IPSEC "
1449 f"on host {nodes[u'DUT1'][u'host']}"
1451 papi_exec.get_replies(err_msg)
1453 # Configure unnumbered interfaces
1454 cmd = "sw_interface_set_unnumbered"
1457 sw_if_index=InterfaceUtil.get_interface_index(
1458 nodes["DUT1"], if1_key
1460 unnumbered_sw_if_index=0,
1462 for i in range(existing_tunnels, n_tunnels):
1463 args["unnumbered_sw_if_index"] = ipip_tunnels[i]
1465 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1468 cmd = "sw_interface_set_flags"
1471 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value,
1473 for i in range(existing_tunnels, n_tunnels):
1474 args["sw_if_index"] = ipip_tunnels[i]
1476 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1478 # Configure IP routes
1479 cmd = "ip_route_add_del"
1480 args = dict(is_add=1, is_multipath=0, route=None)
1481 for i in range(existing_tunnels, n_tunnels):
1482 args["route"] = IPUtil.compose_vpp_route_structure(
1484 (raddr_ip2 + i).compressed,
1485 prefix_len=128 if raddr_ip2.version == 6 else 32,
1486 interface=ipip_tunnels[i],
1489 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1492 f"Failed to add IP routes on host " f"{nodes[u'DUT1'][u'host']}"
1494 papi_exec.get_replies(err_msg)
1499 def _ipsec_create_tunnel_interfaces_dut2_papi(
1513 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1515 This method accesses keys generated by DUT1 method
1516 and does not return anything.
1518 :param nodes: VPP nodes to create tunnel interfaces.
1519 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1520 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1521 IPv4/IPv6 address (ip2).
1522 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1523 interface key from topology file.
1524 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1525 :param crypto_alg: The encryption algorithm name.
1526 :param ckeys: List of encryption keys.
1527 :param integ_alg: The integrity algorithm name.
1528 :param ikeys: List of integrity keys.
1529 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1530 :param addr_incr: IP / IPv6 address incremental step.
1531 :param existing_tunnels: Number of tunnel interfaces before creation.
1532 Useful mainly for reconf tests. Default 0.
1536 :type n_tunnels: int
1537 :type crypto_alg: CryptoAlg
1538 :type ckeys: Sequence[bytes]
1539 :type integ_alg: Optional[IntegAlg]
1540 :type ikeys: Sequence[bytes]
1541 :type addr_incr: int
1543 :type existing_tunnels: int
1545 with PapiSocketExecutor(nodes["DUT2"], is_async=True) as papi_exec:
1546 if not existing_tunnels:
1547 # Set IP address on VPP node 2 interface
1548 cmd = "sw_interface_add_del_address"
1550 sw_if_index=InterfaceUtil.get_interface_index(
1551 nodes["DUT2"], if2_key
1555 prefix=IPUtil.create_prefix_object(
1557 96 if tun_ips["ip2"].version == 6 else 24,
1561 f"Failed to set IP address on interface {if2_key} "
1562 f"on host {nodes[u'DUT2'][u'host']}"
1564 papi_exec.add(cmd, **args).get_replies(err_msg)
1565 # Configure IPIP tunnel interfaces
1566 cmd = "ipip_add_tunnel"
1568 instance=Constants.BITWISE_NON_ZERO,
1573 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1575 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1576 dscp=int(IpDscp.IP_API_DSCP_CS0),
1578 args = dict(tunnel=ipip_tunnel)
1579 ipip_tunnels = [None] * existing_tunnels
1580 for i in range(existing_tunnels, n_tunnels):
1581 args["tunnel"]["src"] = IPAddress.create_ip_address_object(
1584 args["tunnel"]["dst"] = IPAddress.create_ip_address_object(
1585 tun_ips["ip1"] + i * addr_incr
1588 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1591 f"Failed to add IPIP tunnel interfaces on host"
1592 f" {nodes[u'DUT2'][u'host']}"
1594 ipip_tunnels.extend(
1596 reply["sw_if_index"]
1597 for reply in papi_exec.get_replies(err_msg)
1598 if "sw_if_index" in reply
1601 # Configure IPSec SAD entries
1602 cmd = "ipsec_sad_entry_add_v2"
1603 c_key = dict(length=0, data=None)
1604 i_key = dict(length=0, data=None)
1608 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1609 crypto_algorithm=crypto_alg.alg_int_repr,
1611 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1612 integrity_key=i_key,
1618 encap_decap_flags=int(
1619 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1621 dscp=int(IpDscp.IP_API_DSCP_CS0),
1624 udp_src_port=IPSEC_UDP_PORT_DEFAULT,
1625 udp_dst_port=IPSEC_UDP_PORT_DEFAULT,
1626 anti_replay_window_size=IPSEC_REPLAY_WINDOW_DEFAULT,
1628 args = dict(entry=sad_entry)
1629 for i in range(existing_tunnels, n_tunnels):
1631 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1634 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1636 # SAD entry for outband / tx path
1637 args["entry"]["sad_id"] = 100000 + i
1638 args["entry"]["spi"] = spi_d["spi_2"] + i
1640 args["entry"]["crypto_key"]["length"] = len(ckeys[i])
1641 args["entry"]["crypto_key"]["data"] = ckeys[i]
1643 args["entry"]["integrity_key"]["length"] = len(ikeys[i])
1644 args["entry"]["integrity_key"]["data"] = ikeys[i]
1645 args["entry"]["flags"] = int(
1646 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1649 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1651 # SAD entry for inband / rx path
1652 args["entry"]["sad_id"] = i
1653 args["entry"]["spi"] = spi_d["spi_1"] + i
1655 args["entry"]["crypto_key"]["length"] = len(ckeys[i])
1656 args["entry"]["crypto_key"]["data"] = ckeys[i]
1658 args["entry"]["integrity_key"]["length"] = len(ikeys[i])
1659 args["entry"]["integrity_key"]["data"] = ikeys[i]
1660 args["entry"]["flags"] = int(
1661 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1662 | IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1665 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1668 f"Failed to add IPsec SAD entries on host"
1669 f" {nodes[u'DUT2'][u'host']}"
1671 papi_exec.get_replies(err_msg)
1672 # Add protection for tunnels with IPSEC
1673 cmd = "ipsec_tunnel_protect_update"
1676 via_label=MPLS_LABEL_INVALID,
1677 obj_id=Constants.BITWISE_NON_ZERO,
1679 ipsec_tunnel_protect = dict(
1680 sw_if_index=None, nh=n_hop, sa_out=None, n_sa_in=1, sa_in=None
1682 args = dict(tunnel=ipsec_tunnel_protect)
1683 for i in range(existing_tunnels, n_tunnels):
1684 args["tunnel"]["sw_if_index"] = ipip_tunnels[i]
1685 args["tunnel"]["sa_out"] = 100000 + i
1686 args["tunnel"]["sa_in"] = [i]
1688 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1691 f"Failed to add protection for tunnels with IPSEC "
1692 f"on host {nodes[u'DUT2'][u'host']}"
1694 papi_exec.get_replies(err_msg)
1696 if not existing_tunnels:
1697 # Configure IP route
1698 cmd = "ip_route_add_del"
1699 route = IPUtil.compose_vpp_route_structure(
1701 tun_ips["ip1"].compressed,
1702 prefix_len=32 if tun_ips["ip1"].version == 6 else 8,
1704 gateway=(tun_ips["ip2"] - 1).compressed,
1706 args = dict(is_add=1, is_multipath=0, route=route)
1707 papi_exec.add(cmd, **args)
1708 # Configure unnumbered interfaces
1709 cmd = "sw_interface_set_unnumbered"
1712 sw_if_index=InterfaceUtil.get_interface_index(
1713 nodes["DUT2"], if2_key
1715 unnumbered_sw_if_index=0,
1717 for i in range(existing_tunnels, n_tunnels):
1718 args["unnumbered_sw_if_index"] = ipip_tunnels[i]
1720 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1723 cmd = "sw_interface_set_flags"
1726 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value,
1728 for i in range(existing_tunnels, n_tunnels):
1729 args["sw_if_index"] = ipip_tunnels[i]
1731 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1733 # Configure IP routes
1734 cmd = "ip_route_add_del"
1735 args = dict(is_add=1, is_multipath=0, route=None)
1736 for i in range(existing_tunnels, n_tunnels):
1737 args["route"] = IPUtil.compose_vpp_route_structure(
1739 (raddr_ip1 + i).compressed,
1740 prefix_len=128 if raddr_ip1.version == 6 else 32,
1741 interface=ipip_tunnels[i],
1744 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1747 f"Failed to add IP routes " f"on host {nodes[u'DUT2'][u'host']}"
1749 papi_exec.get_replies(err_msg)
1752 def vpp_ipsec_create_tunnel_interfaces(
1767 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1769 Some deployments (e.g. devicetest) need to know the generated keys.
1770 But other deployments (e.g. scale perf test) would get spammed
1771 if we returned keys every time.
1773 :param nodes: VPP nodes to create tunnel interfaces.
1774 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1776 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1778 :param if1_key: VPP node 1 interface key from topology file.
1779 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1780 interface key from topology file.
1781 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1782 :param crypto_alg: The encryption algorithm name.
1783 :param integ_alg: The integrity algorithm name.
1784 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1785 first tunnel in direction node1->node2.
1786 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1787 first tunnel in direction node2->node1.
1788 :param raddr_range: Mask specifying range of Policy selector Remote
1789 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1790 and to 128 in case of IPv6.
1791 :param existing_tunnels: Number of tunnel interfaces before creation.
1792 Useful mainly for reconf tests. Default 0.
1793 :param return_keys: Whether generated keys should be returned.
1795 :type tun_if1_ip_addr: str
1796 :type tun_if2_ip_addr: str
1799 :type n_tunnels: int
1800 :type crypto_alg: CryptoAlg
1801 :type integ_alg: Optonal[IntegAlg]
1802 :type raddr_ip1: string
1803 :type raddr_ip2: string
1804 :type raddr_range: int
1805 :type existing_tunnels: int
1806 :type return_keys: bool
1807 :returns: Ckeys, ikeys, spi_1, spi_2.
1808 :rtype: Optional[List[bytes], List[bytes], int, int]
1810 n_tunnels = int(n_tunnels)
1811 existing_tunnels = int(existing_tunnels)
1812 spi_d = dict(spi_1=100000, spi_2=200000)
1814 ip1=ip_address(tun_if1_ip_addr), ip2=ip_address(tun_if2_ip_addr)
1816 raddr_ip1 = ip_address(raddr_ip1)
1817 raddr_ip2 = ip_address(raddr_ip2)
1819 1 << (128 - raddr_range)
1820 if tun_ips["ip1"].version == 6
1821 else 1 << (32 - raddr_range)
1824 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1837 if "DUT2" in nodes.keys():
1838 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1854 return ckeys, ikeys, spi_d["spi_1"], spi_d["spi_2"]
1858 def _create_ipsec_script_files(dut, instances):
1859 """Create script files for configuring IPsec in containers
1861 :param dut: DUT node on which to create the script files
1862 :param instances: number of containers on DUT node
1864 :type instances: int
1867 for cnf in range(0, instances):
1869 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1871 scripts.append(open(script_filename, "w"))
1875 def _close_and_copy_ipsec_script_files(dut, nodes, instances, scripts):
1876 """Close created scripts and copy them to containers
1878 :param dut: DUT node on which to create the script files
1879 :param nodes: VPP nodes
1880 :param instances: number of containers on DUT node
1881 :param scripts: dictionary holding the script files
1884 :type instances: int
1887 for cnf in range(0, instances):
1888 scripts[cnf].close()
1890 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1892 scp_node(nodes[dut], script_filename, script_filename)
1895 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1907 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1909 :param nodes: VPP nodes to create tunnel interfaces.
1910 :param if1_ip_addr: VPP node 1 interface IP4 address.
1911 :param if2_ip_addr: VPP node 2 interface IP4 address.
1912 :param n_tunnels: Number of tunnell interfaces to create.
1913 :param crypto_alg: The encryption algorithm name.
1914 :param integ_alg: The integrity algorithm name.
1915 :param raddr_ip1: Policy selector remote IPv4 start address for the
1916 first tunnel in direction node1->node2.
1917 :param raddr_ip2: Policy selector remote IPv4 start address for the
1918 first tunnel in direction node2->node1.
1919 :param raddr_range: Mask specifying range of Policy selector Remote
1920 IPv4 addresses. Valid values are from 1 to 32.
1921 :param n_instances: Number of containers.
1923 :type if1_ip_addr: str
1924 :type if2_ip_addr: str
1925 :type n_tunnels: int
1926 :type crypto_alg: CryptoAlg
1927 :type integ_alg: Optional[IntegAlg]
1928 :type raddr_ip1: string
1929 :type raddr_ip2: string
1930 :type raddr_range: int
1931 :type n_instances: int
1935 addr_incr = 1 << (32 - raddr_range)
1937 dut1_scripts = IPsecUtil._create_ipsec_script_files("DUT1", n_instances)
1938 dut2_scripts = IPsecUtil._create_ipsec_script_files("DUT2", n_instances)
1940 for cnf in range(0, n_instances):
1941 dut1_scripts[cnf].write(
1942 "create loopback interface\n" "set interface state loop0 up\n\n"
1944 dut2_scripts[cnf].write(
1945 f"ip route add {if1_ip_addr}/8 via "
1946 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1949 for tnl in range(0, n_tunnels):
1950 cnf = tnl % n_instances
1952 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), "hex"
1956 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), "hex"
1960 f"integ-alg {integ_alg.alg_name} "
1961 f"local-integ-key {ikey} "
1962 f"remote-integ-key {ikey} "
1964 # Configure tunnel end point(s) on left side
1965 dut1_scripts[cnf].write(
1966 "set interface ip address loop0 "
1967 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1968 f"create ipsec tunnel "
1969 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1970 f"local-spi {spi_1 + tnl} "
1971 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1972 f"remote-spi {spi_2 + tnl} "
1973 f"crypto-alg {crypto_alg.alg_name} "
1974 f"local-crypto-key {ckey} "
1975 f"remote-crypto-key {ckey} "
1976 f"instance {tnl // n_instances} "
1979 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1980 f"set interface state ipip{tnl // n_instances} up\n"
1981 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1982 f"via ipip{tnl // n_instances}\n\n"
1984 # Configure tunnel end point(s) on right side
1985 dut2_scripts[cnf].write(
1986 f"set ip neighbor memif1/{cnf + 1} "
1987 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1988 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1989 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1990 f"local-spi {spi_2 + tnl} "
1991 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1992 f"remote-spi {spi_1 + tnl} "
1993 f"crypto-alg {crypto_alg.alg_name} "
1994 f"local-crypto-key {ckey} "
1995 f"remote-crypto-key {ckey} "
1996 f"instance {tnl // n_instances} "
1999 f"set interface unnumbered ipip{tnl // n_instances} "
2000 f"use memif1/{cnf + 1}\n"
2001 f"set interface state ipip{tnl // n_instances} up\n"
2002 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
2003 f"via ipip{tnl // n_instances}\n\n"
2006 IPsecUtil._close_and_copy_ipsec_script_files(
2007 "DUT1", nodes, n_instances, dut1_scripts
2009 IPsecUtil._close_and_copy_ipsec_script_files(
2010 "DUT2", nodes, n_instances, dut2_scripts
2014 def vpp_ipsec_add_multiple_tunnels(
2026 tunnel_addr_incr=True,
2028 """Create multiple IPsec tunnels between two VPP nodes.
2030 :param nodes: VPP nodes to create tunnels.
2031 :param interface1: Interface name or sw_if_index on node 1.
2032 :param interface2: Interface name or sw_if_index on node 2.
2033 :param n_tunnels: Number of tunnels to create.
2034 :param crypto_alg: The encryption algorithm name.
2035 :param integ_alg: The integrity algorithm name.
2036 :param tunnel_ip1: Tunnel node1 IPv4 address.
2037 :param tunnel_ip2: Tunnel node2 IPv4 address.
2038 :param raddr_ip1: Policy selector remote IPv4 start address for the
2039 first tunnel in direction node1->node2.
2040 :param raddr_ip2: Policy selector remote IPv4 start address for the
2041 first tunnel in direction node2->node1.
2042 :param raddr_range: Mask specifying range of Policy selector Remote
2043 IPv4 addresses. Valid values are from 1 to 32.
2044 :param tunnel_addr_incr: Enable or disable tunnel IP address
2047 :type interface1: str or int
2048 :type interface2: str or int
2049 :type n_tunnels: int
2050 :type crypto_alg: CryptoAlg
2051 :type integ_alg: Optional[IntegAlg]
2052 :type tunnel_ip1: str
2053 :type tunnel_ip2: str
2054 :type raddr_ip1: string
2055 :type raddr_ip2: string
2056 :type raddr_range: int
2057 :type tunnel_addr_incr: bool
2067 crypto_key = gen_key(
2068 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2071 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)).decode()
2077 Topology.get_interface_mac(nodes["DUT2"], interface2)
2078 if "DUT2" in nodes.keys()
2079 else Topology.get_interface_mac(nodes["TG"], interface2)
2081 IPsecUtil.vpp_ipsec_set_ip_route(
2092 IPsecUtil.vpp_ipsec_add_spd(nodes["DUT1"], spd_id)
2093 IPsecUtil.vpp_ipsec_spd_add_if(nodes["DUT1"], spd_id, interface1)
2097 if ip_address(tunnel_ip1).version == 6
2100 for i in range(n_tunnels // (addr_incr**2) + 1):
2101 dut1_local_outbound_range = ip_network(
2102 f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8", False
2104 dut1_remote_outbound_range = ip_network(
2105 f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8", False
2108 IPsecUtil.vpp_ipsec_add_spd_entry(
2112 PolicyAction.BYPASS,
2115 laddr_range=dut1_local_outbound_range,
2116 raddr_range=dut1_remote_outbound_range,
2118 IPsecUtil.vpp_ipsec_add_spd_entry(
2122 PolicyAction.BYPASS,
2125 laddr_range=dut1_remote_outbound_range,
2126 raddr_range=dut1_local_outbound_range,
2129 IPsecUtil.vpp_ipsec_add_sad_entries(
2143 IPsecUtil.vpp_ipsec_add_spd_entries(
2147 priority=ObjIncrement(p_lo, 0),
2148 action=PolicyAction.PROTECT,
2150 sa_id=ObjIncrement(sa_id_1, 1),
2151 raddr_range=NetworkIncrement(ip_network(raddr_ip2)),
2154 IPsecUtil.vpp_ipsec_add_sad_entries(
2167 IPsecUtil.vpp_ipsec_add_spd_entries(
2171 priority=ObjIncrement(p_lo, 0),
2172 action=PolicyAction.PROTECT,
2174 sa_id=ObjIncrement(sa_id_2, 1),
2175 raddr_range=NetworkIncrement(ip_network(raddr_ip1)),
2178 if "DUT2" in nodes.keys():
2179 rmac = Topology.get_interface_mac(nodes["DUT1"], interface1)
2180 IPsecUtil.vpp_ipsec_set_ip_route(
2191 IPsecUtil.vpp_ipsec_add_spd(nodes["DUT2"], spd_id)
2192 IPsecUtil.vpp_ipsec_spd_add_if(nodes["DUT2"], spd_id, interface2)
2193 for i in range(n_tunnels // (addr_incr**2) + 1):
2194 dut2_local_outbound_range = ip_network(
2195 f"{ip_address(tunnel_ip1) + i*(addr_incr**3)}/8", False
2197 dut2_remote_outbound_range = ip_network(
2198 f"{ip_address(tunnel_ip2) + i*(addr_incr**3)}/8", False
2201 IPsecUtil.vpp_ipsec_add_spd_entry(
2205 PolicyAction.BYPASS,
2208 laddr_range=dut2_remote_outbound_range,
2209 raddr_range=dut2_local_outbound_range,
2211 IPsecUtil.vpp_ipsec_add_spd_entry(
2215 PolicyAction.BYPASS,
2218 laddr_range=dut2_local_outbound_range,
2219 raddr_range=dut2_remote_outbound_range,
2222 IPsecUtil.vpp_ipsec_add_sad_entries(
2235 IPsecUtil.vpp_ipsec_add_spd_entries(
2239 priority=ObjIncrement(p_lo, 0),
2240 action=PolicyAction.PROTECT,
2242 sa_id=ObjIncrement(sa_id_1, 1),
2243 raddr_range=NetworkIncrement(ip_network(raddr_ip2)),
2246 IPsecUtil.vpp_ipsec_add_sad_entries(
2259 IPsecUtil.vpp_ipsec_add_spd_entries(
2263 priority=ObjIncrement(p_lo, 0),
2264 action=PolicyAction.PROTECT,
2266 sa_id=ObjIncrement(sa_id_2, 1),
2267 raddr_range=NetworkIncrement(ip_network(raddr_ip1)),
2271 def vpp_ipsec_show_all(node):
2272 """Run "show ipsec all" debug CLI command.
2274 :param node: Node to run command on.
2277 PapiSocketExecutor.run_cli_cmd(node, "show ipsec all")
2280 def show_ipsec_security_association(node):
2281 """Show IPSec security association.
2283 :param node: DUT node.
2286 cmd = "ipsec_sa_v5_dump"
2287 PapiSocketExecutor.dump_and_log(node, [cmd])
2290 def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2291 """Ipsec flow enable rss action.
2293 :param node: DUT node.
2294 :param proto: The flow protocol.
2295 :param type: RSS type.
2296 :param function: RSS function.
2302 :returns: flow_index.
2304 # TODO: to be fixed to use full PAPI when it is ready in VPP
2306 f"test flow add src-ip any proto {proto} rss function "
2307 f"{function} rss types {type}"
2309 stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2310 flow_index = stdout.split()[1]
2315 def vpp_create_ipsec_flows_on_dut(
2316 node, n_flows, rx_queues, spi_start, interface
2318 """Create mutiple ipsec flows and enable flows onto interface.
2320 :param node: DUT node.
2321 :param n_flows: Number of flows to create.
2322 :param rx_queues: NUmber of RX queues.
2323 :param spi_start: The start spi.
2324 :param interface: Name of the interface.
2328 :type rx_queues: int
2329 :type spi_start: int
2330 :type interface: str
2331 :returns: flow_index.
2334 for i in range(0, n_flows):
2335 rx_queue = i % rx_queues
2337 flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2338 node, "ESP", spi, "redirect-to-queue", value=rx_queue
2340 FlowUtil.vpp_flow_enable(node, interface, flow_index)