1 # Copyright (c) 2022 Cisco and/or its affiliates.
2 # Copyright (c) 2022 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."""
19 from enum import Enum, IntEnum
21 from ipaddress import ip_network, ip_address
22 from random import choice
23 from string import ascii_letters
25 from resources.libraries.python.Constants import Constants
26 from resources.libraries.python.IncrementUtil import ObjIncrement
27 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
29 from resources.libraries.python.IPAddress import IPAddress
30 from resources.libraries.python.IPUtil import IPUtil, IpDscp, \
31 MPLS_LABEL_INVALID, NetworkIncrement
32 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
33 from resources.libraries.python.ssh import scp_node
34 from resources.libraries.python.topology import Topology, NodeType
35 from resources.libraries.python.VatExecutor import VatExecutor
36 from resources.libraries.python.VPPUtil import VPPUtil
37 from resources.libraries.python.FlowUtil import FlowUtil
40 IPSEC_UDP_PORT_NONE = 0xffff
44 """Generate random string as a key.
46 :param length: Length of generated payload.
48 :returns: The generated payload.
52 choice(ascii_letters) for _ in range(length)
53 ).encode(encoding=u"utf-8")
56 class PolicyAction(Enum):
58 BYPASS = (u"bypass", 0)
59 DISCARD = (u"discard", 1)
60 PROTECT = (u"protect", 3)
62 def __init__(self, policy_name, policy_int_repr):
63 self.policy_name = policy_name
64 self.policy_int_repr = policy_int_repr
67 return self.policy_name
70 return self.policy_int_repr
73 class CryptoAlg(Enum):
74 """Encryption algorithms."""
75 AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
76 AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
77 AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
78 AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
80 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
81 self.alg_name = alg_name
82 self.alg_int_repr = alg_int_repr
83 self.scapy_name = scapy_name
84 self.key_len = key_len
88 """Integrity algorithm."""
89 SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
90 SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
92 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
93 self.alg_name = alg_name
94 self.alg_int_repr = alg_int_repr
95 self.scapy_name = scapy_name
96 self.key_len = key_len
99 class IPsecProto(IntEnum):
100 """IPsec protocol."""
101 IPSEC_API_PROTO_ESP = 50
102 IPSEC_API_PROTO_AH = 51
105 class IPsecSadFlags(IntEnum):
106 """IPsec Security Association Database flags."""
107 IPSEC_API_SAD_FLAG_NONE = 0
108 # Enable extended sequence numbers
109 IPSEC_API_SAD_FLAG_USE_ESN = 0x01
110 # Enable Anti - replay
111 IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02
112 # IPsec tunnel mode if non-zero, else transport mode
113 IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04
114 # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
115 # only valid if is_tunnel is non-zero
116 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08
117 # Enable UDP encapsulation for NAT traversal
118 IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10
119 # IPsec SA is or inbound traffic
120 IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
123 class TunnelEncpaDecapFlags(IntEnum):
124 """Flags controlling tunnel behaviour."""
125 TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
126 # at encap, copy the DF bit of the payload into the tunnel header
127 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
128 # at encap, set the DF bit in the tunnel header
129 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
130 # at encap, copy the DSCP bits of the payload into the tunnel header
131 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
132 # at encap, copy the ECN bit of the payload into the tunnel header
133 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
134 # at decap, copy the ECN bit of the tunnel header into the payload
135 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
138 class TunnelMode(IntEnum):
141 TUNNEL_API_MODE_P2P = 0
143 TUNNEL_API_MODE_MP = 1
147 """IPsec utilities."""
150 def policy_action_bypass():
151 """Return policy action bypass.
153 :returns: PolicyAction enum BYPASS object.
156 return PolicyAction.BYPASS
159 def policy_action_discard():
160 """Return policy action discard.
162 :returns: PolicyAction enum DISCARD object.
165 return PolicyAction.DISCARD
168 def policy_action_protect():
169 """Return policy action protect.
171 :returns: PolicyAction enum PROTECT object.
174 return PolicyAction.PROTECT
177 def crypto_alg_aes_cbc_128():
178 """Return encryption algorithm aes-cbc-128.
180 :returns: CryptoAlg enum AES_CBC_128 object.
183 return CryptoAlg.AES_CBC_128
186 def crypto_alg_aes_cbc_256():
187 """Return encryption algorithm aes-cbc-256.
189 :returns: CryptoAlg enum AES_CBC_256 object.
192 return CryptoAlg.AES_CBC_256
195 def crypto_alg_aes_gcm_128():
196 """Return encryption algorithm aes-gcm-128.
198 :returns: CryptoAlg enum AES_GCM_128 object.
201 return CryptoAlg.AES_GCM_128
204 def crypto_alg_aes_gcm_256():
205 """Return encryption algorithm aes-gcm-256.
207 :returns: CryptoAlg enum AES_GCM_128 object.
210 return CryptoAlg.AES_GCM_256
213 def get_crypto_alg_key_len(crypto_alg):
214 """Return encryption algorithm key length.
216 :param crypto_alg: Encryption algorithm.
217 :type crypto_alg: CryptoAlg
218 :returns: Key length.
221 return crypto_alg.key_len
224 def get_crypto_alg_scapy_name(crypto_alg):
225 """Return encryption algorithm scapy name.
227 :param crypto_alg: Encryption algorithm.
228 :type crypto_alg: CryptoAlg
229 :returns: Algorithm scapy name.
232 return crypto_alg.scapy_name
235 def integ_alg_sha_256_128():
236 """Return integrity algorithm SHA-256-128.
238 :returns: IntegAlg enum SHA_256_128 object.
241 return IntegAlg.SHA_256_128
244 def integ_alg_sha_512_256():
245 """Return integrity algorithm SHA-512-256.
247 :returns: IntegAlg enum SHA_512_256 object.
250 return IntegAlg.SHA_512_256
253 def get_integ_alg_key_len(integ_alg):
254 """Return integrity algorithm key length.
256 None argument is accepted, returning zero.
258 :param integ_alg: Integrity algorithm.
259 :type integ_alg: Optional[IntegAlg]
260 :returns: Key length.
263 return 0 if integ_alg is None else integ_alg.key_len
266 def get_integ_alg_scapy_name(integ_alg):
267 """Return integrity algorithm scapy name.
269 :param integ_alg: Integrity algorithm.
270 :type integ_alg: IntegAlg
271 :returns: Algorithm scapy name.
274 return integ_alg.scapy_name
277 def ipsec_proto_esp():
278 """Return IPSec protocol ESP.
280 :returns: IPsecProto enum ESP object.
283 return int(IPsecProto.IPSEC_API_PROTO_ESP)
286 def ipsec_proto_ah():
287 """Return IPSec protocol AH.
289 :returns: IPsecProto enum AH object.
292 return int(IPsecProto.IPSEC_API_PROTO_AH)
295 def vpp_ipsec_select_backend(node, protocol, index=1):
296 """Select IPsec backend.
298 :param node: VPP node to select IPsec backend on.
299 :param protocol: IPsec protocol.
300 :param index: Backend index.
302 :type protocol: IPsecProto
304 :raises RuntimeError: If failed to select IPsec backend or if no API
307 cmd = u"ipsec_select_backend"
308 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
313 with PapiSocketExecutor(node) as papi_exec:
314 papi_exec.add(cmd, **args).get_reply(err_msg)
317 def vpp_ipsec_set_async_mode(node, async_enable=1):
318 """Set IPsec async mode on|off.
320 :param node: VPP node to set IPsec async mode.
321 :param async_enable: Async mode on or off.
323 :type async_enable: int
324 :raises RuntimeError: If failed to set IPsec async mode or if no API
327 cmd = u"ipsec_set_async_mode"
328 err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
330 async_enable=async_enable
332 with PapiSocketExecutor(node) as papi_exec:
333 papi_exec.add(cmd, **args).get_reply(err_msg)
336 def vpp_ipsec_crypto_sw_scheduler_set_worker(
337 node, workers, crypto_enable=False):
338 """Enable or disable crypto on specific vpp worker threads.
340 :param node: VPP node to enable or disable crypto for worker threads.
341 :param workers: List of VPP thread numbers.
342 :param crypto_enable: Disable or enable crypto work.
344 :type workers: Iterable[int]
345 :type crypto_enable: bool
346 :raises RuntimeError: If failed to enable or disable crypto for worker
347 thread or if no API reply received.
349 for worker in workers:
350 cmd = u"crypto_sw_scheduler_set_worker"
351 err_msg = f"Failed to disable/enable crypto for worker thread " \
352 f"on host {node[u'host']}"
354 worker_index=worker - 1,
355 crypto_enable=crypto_enable
357 with PapiSocketExecutor(node) as papi_exec:
358 papi_exec.add(cmd, **args).get_reply(err_msg)
361 def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
362 nodes, workers, crypto_enable=False):
363 """Enable or disable crypto on specific vpp worker threads.
365 :param node: VPP node to enable or disable crypto for worker threads.
366 :param workers: List of VPP thread numbers.
367 :param crypto_enable: Disable or enable crypto work.
369 :type workers: Iterable[int]
370 :type crypto_enable: bool
371 :raises RuntimeError: If failed to enable or disable crypto for worker
372 thread or if no API reply received.
374 for node in nodes.values():
375 if node[u"type"] == NodeType.DUT:
376 thread_data = VPPUtil.vpp_show_threads(node)
377 worker_cnt = len(thread_data) - 1
381 for item in thread_data:
382 if str(item.cpu_id) in workers.split(u","):
383 worker_ids.append(item.id)
385 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
386 node, workers=worker_ids, crypto_enable=crypto_enable
390 def vpp_ipsec_add_sad_entry(
391 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
392 integ_key=u"", tunnel_src=None, tunnel_dst=None):
393 """Create Security Association Database entry on the VPP node.
395 :param node: VPP node to add SAD entry on.
396 :param sad_id: SAD entry ID.
397 :param spi: Security Parameter Index of this SAD entry.
398 :param crypto_alg: The encryption algorithm name.
399 :param crypto_key: The encryption key string.
400 :param integ_alg: The integrity algorithm name.
401 :param integ_key: The integrity key string.
402 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
403 specified ESP transport mode is used.
404 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
405 not specified ESP transport mode is used.
409 :type crypto_alg: CryptoAlg
410 :type crypto_key: str
411 :type integ_alg: Optional[IntegAlg]
413 :type tunnel_src: str
414 :type tunnel_dst: str
416 if isinstance(crypto_key, str):
417 crypto_key = crypto_key.encode(encoding=u"utf-8")
418 if isinstance(integ_key, str):
419 integ_key = integ_key.encode(encoding=u"utf-8")
421 length=len(crypto_key),
425 length=len(integ_key),
426 data=integ_key if integ_key else 0
429 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
430 if tunnel_src and tunnel_dst:
431 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
432 src_addr = ip_address(tunnel_src)
433 dst_addr = ip_address(tunnel_dst)
434 if src_addr.version == 6:
436 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
441 cmd = u"ipsec_sad_entry_add"
442 err_msg = f"Failed to add Security Association Database entry " \
443 f"on host {node[u'host']}"
447 crypto_algorithm=crypto_alg.alg_int_repr,
449 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
456 encap_decap_flags=int(
457 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
459 dscp=int(IpDscp.IP_API_DSCP_CS0),
461 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
462 udp_src_port=4500, # default value in api
463 udp_dst_port=4500 # default value in api
465 args = dict(entry=sad_entry)
466 with PapiSocketExecutor(node) as papi_exec:
467 papi_exec.add(cmd, **args).get_reply(err_msg)
470 def vpp_ipsec_add_sad_entries(
471 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
472 integ_alg=None, integ_key=u"", tunnel_src=None,tunnel_dst=None,
473 tunnel_addr_incr=True):
474 """Create multiple Security Association Database entries on VPP node.
476 :param node: VPP node to add SAD entry on.
477 :param n_entries: Number of SAD entries to be created.
478 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
480 :param spi: Security Parameter Index of first SAD entry. All subsequent
481 SAD entries will have spi incremented by 1.
482 :param crypto_alg: The encryption algorithm name.
483 :param crypto_key: The encryption key string.
484 :param integ_alg: The integrity algorithm name.
485 :param integ_key: The integrity key string.
486 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
487 specified ESP transport mode is used.
488 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
489 not specified ESP transport mode is used.
490 :param tunnel_addr_incr: Enable or disable tunnel IP address
496 :type crypto_alg: CryptoAlg
497 :type crypto_key: str
498 :type integ_alg: Optional[IntegAlg]
500 :type tunnel_src: str
501 :type tunnel_dst: str
502 :type tunnel_addr_incr: bool
504 if isinstance(crypto_key, str):
505 crypto_key = crypto_key.encode(encoding=u"utf-8")
506 if isinstance(integ_key, str):
507 integ_key = integ_key.encode(encoding=u"utf-8")
508 if tunnel_src and tunnel_dst:
509 src_addr = ip_address(tunnel_src)
510 dst_addr = ip_address(tunnel_dst)
516 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
521 if int(n_entries) > 10:
522 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
524 with open(tmp_filename, 'w') as tmp_file:
525 for i in range(n_entries):
526 integ = f"integ-alg {integ_alg.alg_name} " \
527 f"integ-key {integ_key.hex()}" \
528 if integ_alg else u""
529 tunnel = f"tunnel src {src_addr + i * addr_incr} " \
530 f"tunnel dst {dst_addr + i * addr_incr}" \
531 if tunnel_src and tunnel_dst else u""
532 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
533 f"crypto-alg {crypto_alg.alg_name} " \
534 f"crypto-key {crypto_key.hex()} " \
535 f"{integ} {tunnel}\n"
539 tmp_filename, node, timeout=300, json_out=False,
542 os.remove(tmp_filename)
546 length=len(crypto_key),
550 length=len(integ_key),
551 data=integ_key if integ_key else 0
554 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
555 if tunnel_src and tunnel_dst:
556 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
557 if src_addr.version == 6:
559 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
562 cmd = u"ipsec_sad_entry_add"
563 err_msg = f"Failed to add Security Association Database entry " \
564 f"on host {node[u'host']}"
569 crypto_algorithm=crypto_alg.alg_int_repr,
571 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
578 encap_decap_flags=int(
579 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
581 dscp=int(IpDscp.IP_API_DSCP_CS0),
583 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
584 udp_src_port=4500, # default value in api
585 udp_dst_port=4500, # default value in api
587 args = dict(entry=sad_entry)
588 with PapiSocketExecutor(node) as papi_exec:
589 for i in range(n_entries):
590 args[u"entry"][u"sad_id"] = int(sad_id) + i
591 args[u"entry"][u"spi"] = int(spi) + i
592 args[u"entry"][u"tunnel"][u"src"] = (
593 str(src_addr + i * addr_incr)
594 if tunnel_src and tunnel_dst else src_addr
596 args[u"entry"][u"tunnel"][u"dst"] = (
597 str(dst_addr + i * addr_incr)
598 if tunnel_src and tunnel_dst else dst_addr
600 history = bool(not 1 < i < n_entries - 2)
601 papi_exec.add(cmd, history=history, **args)
602 papi_exec.get_replies(err_msg)
605 def vpp_ipsec_set_ip_route(
606 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
607 raddr_range, dst_mac=None):
608 """Set IP address and route on interface.
610 :param node: VPP node to add config on.
611 :param n_tunnels: Number of tunnels to create.
612 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
613 :param traffic_addr: Traffic destination IP address to route.
614 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
615 :param interface: Interface key on node 1.
616 :param raddr_range: Mask specifying range of Policy selector Remote IP
617 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
619 :param dst_mac: The MAC address of destination tunnels.
622 :type tunnel_src: str
623 :type traffic_addr: str
624 :type tunnel_dst: str
626 :type raddr_range: int
629 tunnel_src = ip_address(tunnel_src)
630 tunnel_dst = ip_address(tunnel_dst)
631 traffic_addr = ip_address(traffic_addr)
632 tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
633 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
634 else 1 << (32 - raddr_range)
636 if int(n_tunnels) > 10:
637 tmp_filename = u"/tmp/ipsec_set_ip.script"
639 with open(tmp_filename, 'w') as tmp_file:
640 if_name = Topology.get_interface_name(node, interface)
641 for i in range(n_tunnels):
642 tunnel_dst_addr = tunnel_dst + i * addr_incr
643 conf = f"exec set interface ip address {if_name} " \
644 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
645 f"exec ip route add {traffic_addr + i}/" \
646 f"{tunnel_dst_prefix} " \
647 f"via {tunnel_dst_addr} {if_name}\n" \
648 f"exec ip route add {tunnel_dst_addr}/" \
649 f"{tunnel_dst_prefix} " \
650 f"via {tunnel_dst_addr} {if_name}\n"
652 conf = f"{conf}exec set ip neighbor {if_name} " \
653 f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
656 VatExecutor().execute_script(
657 tmp_filename, node, timeout=300, json_out=False,
660 os.remove(tmp_filename)
663 cmd1 = u"sw_interface_add_del_address"
665 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
670 cmd2 = u"ip_route_add_del"
676 cmd3 = u"ip_neighbor_add_del"
680 sw_if_index=Topology.get_interface_sw_index(node, interface),
682 mac_address=str(dst_mac),
686 err_msg = f"Failed to configure IP addresses, IP routes and " \
687 f"IP neighbor on interface {interface} on host {node[u'host']}" \
689 else f"Failed to configure IP addresses and IP routes " \
690 f"on interface {interface} on host {node[u'host']}"
692 with PapiSocketExecutor(node) as papi_exec:
693 for i in range(n_tunnels):
694 tunnel_dst_addr = tunnel_dst + i * addr_incr
695 args1[u"prefix"] = IPUtil.create_prefix_object(
696 tunnel_src + i * addr_incr, raddr_range
698 args2[u"route"] = IPUtil.compose_vpp_route_structure(
699 node, traffic_addr + i,
700 prefix_len=tunnel_dst_prefix,
701 interface=interface, gateway=tunnel_dst_addr
703 history = bool(not 1 < i < n_tunnels - 2)
704 papi_exec.add(cmd1, history=history, **args1).\
705 add(cmd2, history=history, **args2)
707 args2[u"route"] = IPUtil.compose_vpp_route_structure(
708 node, tunnel_dst_addr,
709 prefix_len=tunnel_dst_prefix,
710 interface=interface, gateway=tunnel_dst_addr
712 papi_exec.add(cmd2, history=history, **args2)
715 args3[u"neighbor"][u"ip_address"] = ip_address(
718 papi_exec.add(cmd3, history=history, **args3)
719 papi_exec.get_replies(err_msg)
722 def vpp_ipsec_add_spd(node, spd_id):
723 """Create Security Policy Database on the VPP node.
725 :param node: VPP node to add SPD on.
726 :param spd_id: SPD ID.
730 cmd = u"ipsec_spd_add_del"
731 err_msg = f"Failed to add Security Policy Database " \
732 f"on host {node[u'host']}"
737 with PapiSocketExecutor(node) as papi_exec:
738 papi_exec.add(cmd, **args).get_reply(err_msg)
741 def vpp_ipsec_spd_add_if(node, spd_id, interface):
742 """Add interface to the Security Policy Database.
744 :param node: VPP node.
745 :param spd_id: SPD ID to add interface on.
746 :param interface: Interface name or sw_if_index.
749 :type interface: str or int
751 cmd = u"ipsec_interface_add_del_spd"
752 err_msg = f"Failed to add interface {interface} to Security Policy " \
753 f"Database {spd_id} on host {node[u'host']}"
756 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
759 with PapiSocketExecutor(node) as papi_exec:
760 papi_exec.add(cmd, **args).get_reply(err_msg)
763 def vpp_ipsec_create_spds_match_nth_entry(
764 node, dir1_interface, dir2_interface, entry_amount,
765 local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
766 inbound=False, bidirectional=True):
767 """Create one matching SPD entry for inbound or outbound traffic on
768 a DUT for each traffic direction and also create entry_amount - 1
769 non-matching SPD entries. Create a Security Policy Database on each
770 outbound interface where these entries will be configured.
771 The matching SPD entry will have the lowest priority, input action and
772 will be configured to match the IP flow. The non-matching entries will
773 be the same, except with higher priority and non-matching IP flows.
775 Action Protect is currently not supported.
777 :param node: VPP node to configured the SPDs and their entries.
778 :param dir1_interface: The interface in direction 1 where the entries
780 :param dir2_interface: The interface in direction 2 where the entries
782 :param entry_amount: The number of SPD entries to configure. If
783 entry_amount == 1, no non-matching entries will be configured.
784 :param local_addr_range: Matching local address range in direction 1
785 in format IP/prefix or IP/mask. If no mask is provided, it's
786 considered to be /32.
787 :param remote_addr_range: Matching remote address range in
788 direction 1 in format IP/prefix or IP/mask. If no mask is
789 provided, it's considered to be /32.
790 :param action: Policy action.
791 :param inbound: If True policy is for inbound traffic, otherwise
793 :param bidirectional: When True, will create SPDs in both directions
794 of traffic. When False, only in one direction.
796 :type dir1_interface: Union[string, int]
797 :type dir2_interface: Union[string, int]
798 :type entry_amount: int
799 :type local_addr_range:
800 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
801 :type remote_addr_range:
802 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
803 :type action: IPsecUtil.PolicyAction
805 :type bidirectional: bool
806 :raises NotImplementedError: When the action is PolicyAction.PROTECT.
809 if action == PolicyAction.PROTECT:
810 raise NotImplementedError('Policy action PROTECT is not supported.')
814 matching_priority = 1
816 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
817 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
818 # matching entry direction 1
819 IPsecUtil.vpp_ipsec_add_spd_entry(
820 node, spd_id_dir1, matching_priority, action,
821 inbound=inbound, laddr_range=local_addr_range,
822 raddr_range=remote_addr_range
826 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
827 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
829 # matching entry direction 2, the address ranges are switched
830 IPsecUtil.vpp_ipsec_add_spd_entry(
831 node, spd_id_dir2, matching_priority, action,
832 inbound=inbound, laddr_range=remote_addr_range,
833 raddr_range=local_addr_range
836 # non-matching entries
837 no_match_entry_amount = entry_amount - 1
838 if no_match_entry_amount > 0:
839 # create a NetworkIncrement representation of the network,
840 # then skip the matching network
841 no_match_local_addr_range = NetworkIncrement(
842 ip_network(local_addr_range)
844 next(no_match_local_addr_range)
846 no_match_remote_addr_range = NetworkIncrement(
847 ip_network(remote_addr_range)
849 next(no_match_remote_addr_range)
851 # non-matching entries direction 1
852 IPsecUtil.vpp_ipsec_add_spd_entries(
853 node, no_match_entry_amount, spd_id_dir1,
854 ObjIncrement(matching_priority + 1, 1), action,
855 inbound=inbound, laddr_range=no_match_local_addr_range,
856 raddr_range=no_match_remote_addr_range
860 # reset the networks so that we're using a unified config
861 # the address ranges are switched
862 no_match_remote_addr_range = NetworkIncrement(
863 ip_network(local_addr_range)
865 next(no_match_remote_addr_range)
867 no_match_local_addr_range = NetworkIncrement(
868 ip_network(remote_addr_range)
870 next(no_match_local_addr_range)
871 # non-matching entries direction 2
872 IPsecUtil.vpp_ipsec_add_spd_entries(
873 node, no_match_entry_amount, spd_id_dir2,
874 ObjIncrement(matching_priority + 1, 1), action,
875 inbound=inbound, laddr_range=no_match_local_addr_range,
876 raddr_range=no_match_remote_addr_range
879 IPsecUtil.vpp_ipsec_show_all(node)
882 def vpp_ipsec_add_spd_entry(
883 node, spd_id, priority, action, inbound=True, sa_id=None,
884 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
885 rport_range=None, is_ipv6=False):
886 """Create Security Policy Database entry on the VPP node.
888 :param node: VPP node to add SPD entry on.
889 :param spd_id: SPD ID to add entry on.
890 :param priority: SPD entry priority, higher number = higher priority.
891 :param action: Policy action.
892 :param inbound: If True policy is for inbound traffic, otherwise
894 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
895 :param proto: Policy selector next layer protocol number.
896 :param laddr_range: Policy selector local IPv4 or IPv6 address range
897 in format IP/prefix or IP/mask. If no mask is provided,
898 it's considered to be /32.
899 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
900 in format IP/prefix or IP/mask. If no mask is provided,
901 it's considered to be /32.
902 :param lport_range: Policy selector local TCP/UDP port range in format
903 <port_start>-<port_end>.
904 :param rport_range: Policy selector remote TCP/UDP port range in format
905 <port_start>-<port_end>.
906 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
907 not defined so it will default to address ::/0, otherwise False.
911 :type action: IPsecUtil.PolicyAction
915 :type laddr_range: string
916 :type raddr_range: string
917 :type lport_range: string
918 :type rport_range: string
921 if laddr_range is None:
922 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
924 if raddr_range is None:
925 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
927 local_net = ip_network(laddr_range, strict=False)
928 remote_net = ip_network(raddr_range, strict=False)
930 cmd = u"ipsec_spd_entry_add_del"
931 err_msg = f"Failed to add entry to Security Policy Database " \
932 f"{spd_id} on host {node[u'host']}"
936 priority=int(priority),
937 is_outbound=not inbound,
938 sa_id=int(sa_id) if sa_id else 0,
940 protocol=int(proto) if proto else 0,
941 remote_address_start=IPAddress.create_ip_address_object(
942 remote_net.network_address
944 remote_address_stop=IPAddress.create_ip_address_object(
945 remote_net.broadcast_address
947 local_address_start=IPAddress.create_ip_address_object(
948 local_net.network_address
950 local_address_stop=IPAddress.create_ip_address_object(
951 local_net.broadcast_address
953 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
955 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
957 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
959 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
966 with PapiSocketExecutor(node) as papi_exec:
967 papi_exec.add(cmd, **args).get_reply(err_msg)
970 def vpp_ipsec_add_spd_entries(
971 node, n_entries, spd_id, priority, action, inbound, sa_id=None,
972 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
973 rport_range=None, is_ipv6=False):
974 """Create multiple Security Policy Database entries on the VPP node.
976 :param node: VPP node to add SPD entries on.
977 :param n_entries: Number of SPD entries to be added.
978 :param spd_id: SPD ID to add entries on.
979 :param priority: SPD entries priority, higher number = higher priority.
980 :param action: Policy action.
981 :param inbound: If True policy is for inbound traffic, otherwise
983 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
984 :param proto: Policy selector next layer protocol number.
985 :param laddr_range: Policy selector local IPv4 or IPv6 address range
986 in format IP/prefix or IP/mask. If no mask is provided,
987 it's considered to be /32.
988 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
989 in format IP/prefix or IP/mask. If no mask is provided,
990 it's considered to be /32.
991 :param lport_range: Policy selector local TCP/UDP port range in format
992 <port_start>-<port_end>.
993 :param rport_range: Policy selector remote TCP/UDP port range in format
994 <port_start>-<port_end>.
995 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
996 not defined so it will default to address ::/0, otherwise False.
1000 :type priority: IPsecUtil.ObjIncrement
1001 :type action: IPsecUtil.PolicyAction
1003 :type sa_id: IPsecUtil.ObjIncrement
1005 :type laddr_range: IPsecUtil.NetworkIncrement
1006 :type raddr_range: IPsecUtil.NetworkIncrement
1007 :type lport_range: string
1008 :type rport_range: string
1011 if laddr_range is None:
1012 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1013 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1015 if raddr_range is None:
1016 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1017 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1019 lport_range_start = 0
1020 lport_range_stop = 65535
1022 lport_range_start, lport_range_stop = lport_range.split('-')
1024 rport_range_start = 0
1025 rport_range_stop = 65535
1027 rport_range_start, rport_range_stop = rport_range.split('-')
1029 if int(n_entries) > 10:
1030 tmp_filename = f"/tmp/ipsec_spd_{spd_id}_add_del_entry.script"
1032 with open(tmp_filename, 'w') as tmp_file:
1033 for _ in range(n_entries):
1034 direction = u'inbound' if inbound else u'outbound'
1035 sa = f' sa {sa_id.inc_fmt()}' if sa_id is not None else ''
1036 protocol = f' protocol {protocol}' if proto else ''
1037 local_port_range = f' local-port-range ' \
1038 f'{lport_range_start} - {lport_range_stop}' \
1039 if lport_range else ''
1040 remote_port_range = f' remote-port-range ' \
1041 f'{rport_range_start} - {rport_range_stop}' \
1042 if rport_range else ''
1044 spd_cfg = f"exec ipsec policy add spd {spd_id} " \
1045 f"priority {priority.inc_fmt()} {direction}" \
1046 f"{protocol} action {action}{sa} " \
1047 f"local-ip-range {laddr_range.inc_fmt()} " \
1048 f"remote-ip-range {raddr_range.inc_fmt()}" \
1049 f"{local_port_range}{remote_port_range}\n"
1051 tmp_file.write(spd_cfg)
1053 VatExecutor().execute_script(
1054 tmp_filename, node, timeout=300, json_out=False,
1055 copy_on_execute=True
1057 os.remove(tmp_filename)
1060 for _ in range(n_entries):
1061 IPsecUtil.vpp_ipsec_add_spd_entry(
1062 node, spd_id, next(priority), action, inbound,
1063 next(sa_id) if sa_id is not None else sa_id,
1064 proto, next(laddr_range), next(raddr_range), lport_range,
1065 rport_range, is_ipv6
1069 def _ipsec_create_tunnel_interfaces_dut1_vat(
1070 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1071 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1072 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
1074 Generate random keys and return them (so DUT2 or TG can decrypt).
1076 :param nodes: VPP nodes to create tunnel interfaces.
1077 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1078 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1079 IPv4/IPv6 address (ip2).
1080 :param if1_key: VPP node 1 interface key from topology file.
1081 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1082 interface key from topology file.
1083 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1084 :param crypto_alg: The encryption algorithm name.
1085 :param integ_alg: The integrity algorithm name.
1086 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1087 first tunnel in direction node2->node1.
1088 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1089 :param addr_incr: IP / IPv6 address incremental step.
1090 :param existing_tunnels: Number of tunnel interfaces before creation.
1091 Useful mainly for reconf tests. Default 0.
1096 :type n_tunnels: int
1097 :type crypto_alg: CryptoAlg
1098 :type integ_alg: Optional[IntegAlg]
1099 :type raddr_ip2: IPv4Address or IPv6Address
1100 :type addr_incr: int
1102 :type existing_tunnels: int
1103 :returns: Generated ckeys and ikeys.
1104 :rtype: List[bytes], List[bytes]
1106 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
1107 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
1109 ckeys = [bytes()] * existing_tunnels
1110 ikeys = [bytes()] * existing_tunnels
1113 with open(tmp_fn1, u"w") as tmp_f1:
1114 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
1115 if u"DUT2" in nodes.keys() \
1116 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
1117 if not existing_tunnels:
1119 f"exec create loopback interface\n"
1120 f"exec set interface state loop0 up\n"
1121 f"exec set interface ip address {if1_n} "
1122 f"{tun_ips[u'ip2'] - 1}/"
1123 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
1124 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
1127 for i in range(existing_tunnels, n_tunnels):
1129 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1132 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1135 integ = f"integ-alg {integ_alg.alg_name} " \
1136 f"integ-key {ikeys[i].hex()} "
1140 f"exec set interface ip address loop0 "
1141 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
1142 f"exec create ipip tunnel "
1143 f"src {tun_ips[u'ip1'] + i * addr_incr} "
1144 f"dst {tun_ips[u'ip2']} "
1146 f"exec ipsec sa add {i} "
1147 f"spi {spi_d[u'spi_1'] + i} "
1148 f"crypto-alg {crypto_alg.alg_name} "
1149 f"crypto-key {ckeys[i].hex()} "
1152 f"exec ipsec sa add {100000 + i} "
1153 f"spi {spi_d[u'spi_2'] + i} "
1154 f"crypto-alg {crypto_alg.alg_name} "
1155 f"crypto-key {ckeys[i].hex()} "
1158 f"exec ipsec tunnel protect ipip{i} "
1160 f"sa-in {100000 + i} "
1164 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1165 copy_on_execute=True,
1166 history=bool(n_tunnels < 100)
1170 with open(tmp_fn1, 'w') as tmp_f1:
1171 for i in range(existing_tunnels, n_tunnels):
1173 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
1174 f"exec set interface state ipip{i} up\n"
1175 f"exec ip route add "
1176 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
1180 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1181 copy_on_execute=True,
1182 history=bool(n_tunnels < 100)
1189 def _ipsec_create_tunnel_interfaces_dut2_vat(
1190 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1191 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1192 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
1194 This method accesses keys generated by DUT1 method
1195 and does not return anything.
1197 :param nodes: VPP nodes to create tunnel interfaces.
1198 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1199 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1200 IPv4/IPv6 address (ip2).
1201 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1202 interface key from topology file.
1203 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1204 :param crypto_alg: The encryption algorithm name.
1205 :param ckeys: List of encryption keys.
1206 :param integ_alg: The integrity algorithm name.
1207 :param ikeys: List of integrity keys.
1208 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1209 :param addr_incr: IP / IPv6 address incremental step.
1210 :param existing_tunnels: Number of tunnel interfaces before creation.
1211 Useful mainly for reconf tests. Default 0.
1215 :type n_tunnels: int
1216 :type crypto_alg: CryptoAlg
1217 :type ckeys: Sequence[bytes]
1218 :type integ_alg: Optional[IntegAlg]
1219 :type ikeys: Sequence[bytes]
1220 :type addr_incr: int
1222 :type existing_tunnels: int
1224 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1225 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1228 with open(tmp_fn2, 'w') as tmp_f2:
1229 if not existing_tunnels:
1231 f"exec set interface ip address {if2_n}"
1232 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1234 for i in range(existing_tunnels, n_tunnels):
1236 integ = f"integ-alg {integ_alg.alg_name} " \
1237 f"integ-key {ikeys[i].hex()} "
1241 f"exec create ipip tunnel "
1242 f"src {tun_ips[u'ip2']} "
1243 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1245 f"exec ipsec sa add {100000 + i} "
1246 f"spi {spi_d[u'spi_2'] + i} "
1247 f"crypto-alg {crypto_alg.alg_name} "
1248 f"crypto-key {ckeys[i].hex()} "
1251 f"exec ipsec sa add {i} "
1252 f"spi {spi_d[u'spi_1'] + i} "
1253 f"crypto-alg {crypto_alg.alg_name} "
1254 f"crypto-key {ckeys[i].hex()} "
1257 f"exec ipsec tunnel protect ipip{i} "
1258 f"sa-out {100000 + i} "
1263 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1264 copy_on_execute=True,
1265 history=bool(n_tunnels < 100)
1269 with open(tmp_fn2, 'w') as tmp_f2:
1270 if not existing_tunnels:
1272 f"exec ip route add {tun_ips[u'ip1']}/8 "
1273 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1275 for i in range(existing_tunnels, n_tunnels):
1277 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1278 f"exec set interface state ipip{i} up\n"
1279 f"exec ip route add "
1280 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1284 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1285 copy_on_execute=True,
1286 history=bool(n_tunnels < 100)
1291 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1292 """Create loopback interface and set IP address on VPP node 1 interface
1295 :param nodes: VPP nodes to create tunnel interfaces.
1296 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1297 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1298 IPv4/IPv6 address (ip2).
1299 :param if1_key: VPP node 1 interface key from topology file.
1300 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1301 interface key from topology file.
1307 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1308 # Create loopback interface on DUT1, set it to up state
1309 cmd = u"create_loopback_instance"
1315 err_msg = f"Failed to create loopback interface " \
1316 f"on host {nodes[u'DUT1'][u'host']}"
1317 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1318 get_sw_if_index(err_msg)
1319 cmd = u"sw_interface_set_flags"
1321 sw_if_index=loop_sw_if_idx,
1322 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1324 err_msg = f"Failed to set loopback interface state up " \
1325 f"on host {nodes[u'DUT1'][u'host']}"
1326 papi_exec.add(cmd, **args).get_reply(err_msg)
1327 # Set IP address on VPP node 1 interface
1328 cmd = u"sw_interface_add_del_address"
1330 sw_if_index=InterfaceUtil.get_interface_index(
1331 nodes[u"DUT1"], if1_key
1335 prefix=IPUtil.create_prefix_object(
1336 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1340 err_msg = f"Failed to set IP address on interface {if1_key} " \
1341 f"on host {nodes[u'DUT1'][u'host']}"
1342 papi_exec.add(cmd, **args).get_reply(err_msg)
1343 cmd2 = u"ip_neighbor_add_del"
1347 sw_if_index=Topology.get_interface_sw_index(
1348 nodes[u"DUT1"], if1_key
1352 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1353 if u"DUT2" in nodes.keys()
1354 else Topology.get_interface_mac(
1355 nodes[u"TG"], if2_key
1358 ip_address=tun_ips[u"ip2"].compressed
1361 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1362 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1364 return loop_sw_if_idx
1367 def _ipsec_create_tunnel_interfaces_dut1_papi(
1368 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1369 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1370 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1372 Generate random keys and return them (so DUT2 or TG can decrypt).
1374 :param nodes: VPP nodes to create tunnel interfaces.
1375 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1376 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1377 IPv4/IPv6 address (ip2).
1378 :param if1_key: VPP node 1 interface key from topology file.
1379 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1380 interface key from topology file.
1381 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1382 :param crypto_alg: The encryption algorithm name.
1383 :param integ_alg: The integrity algorithm name.
1384 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1385 first tunnel in direction node2->node1.
1386 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1387 :param addr_incr: IP / IPv6 address incremental step.
1388 :param existing_tunnels: Number of tunnel interfaces before creation.
1389 Useful mainly for reconf tests. Default 0.
1394 :type n_tunnels: int
1395 :type crypto_alg: CryptoAlg
1396 :type integ_alg: Optional[IntegAlg]
1397 :type raddr_ip2: IPv4Address or IPv6Address
1398 :type addr_incr: int
1400 :type existing_tunnels: int
1401 :returns: Generated ckeys and ikeys.
1402 :rtype: List[bytes], List[bytes]
1404 if not existing_tunnels:
1405 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1406 nodes, tun_ips, if1_key, if2_key
1409 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1410 nodes[u"DUT1"], u"loop0"
1412 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1413 # Configure IP addresses on loop0 interface
1414 cmd = u"sw_interface_add_del_address"
1416 sw_if_index=loop_sw_if_idx,
1421 for i in range(existing_tunnels, n_tunnels):
1422 args[u"prefix"] = IPUtil.create_prefix_object(
1423 tun_ips[u"ip1"] + i * addr_incr,
1424 128 if tun_ips[u"ip1"].version == 6 else 32
1427 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1429 # Configure IPIP tunnel interfaces
1430 cmd = u"ipip_add_tunnel"
1432 instance=Constants.BITWISE_NON_ZERO,
1437 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1439 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1440 dscp=int(IpDscp.IP_API_DSCP_CS0)
1445 ipip_tunnels = [None] * existing_tunnels
1446 for i in range(existing_tunnels, n_tunnels):
1447 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1448 tun_ips[u"ip1"] + i * addr_incr
1450 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1454 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1456 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1457 f" {nodes[u'DUT1'][u'host']}"
1458 ipip_tunnels.extend(
1460 reply[u"sw_if_index"]
1461 for reply in papi_exec.get_replies(err_msg)
1462 if u"sw_if_index" in reply
1465 # Configure IPSec SAD entries
1466 ckeys = [bytes()] * existing_tunnels
1467 ikeys = [bytes()] * existing_tunnels
1468 cmd = u"ipsec_sad_entry_add"
1480 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1481 crypto_algorithm=crypto_alg.alg_int_repr,
1483 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1484 integrity_key=i_key,
1490 encap_decap_flags=int(
1491 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1493 dscp=int(IpDscp.IP_API_DSCP_CS0),
1496 udp_src_port=IPSEC_UDP_PORT_NONE,
1497 udp_dst_port=IPSEC_UDP_PORT_NONE,
1499 args = dict(entry=sad_entry)
1500 for i in range(existing_tunnels, n_tunnels):
1502 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1505 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1507 # SAD entry for outband / tx path
1508 args[u"entry"][u"sad_id"] = i
1509 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1511 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1512 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1514 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1515 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1516 args[u"entry"][u"flags"] = int(
1517 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1520 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1522 # SAD entry for inband / rx path
1523 args[u"entry"][u"sad_id"] = 100000 + i
1524 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1526 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1527 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1529 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1530 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1531 args[u"entry"][u"flags"] = int(
1532 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1533 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1536 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1538 err_msg = f"Failed to add IPsec SAD entries on host" \
1539 f" {nodes[u'DUT1'][u'host']}"
1540 papi_exec.get_replies(err_msg)
1541 # Add protection for tunnels with IPSEC
1542 cmd = u"ipsec_tunnel_protect_update"
1545 via_label=MPLS_LABEL_INVALID,
1546 obj_id=Constants.BITWISE_NON_ZERO
1548 ipsec_tunnel_protect = dict(
1556 tunnel=ipsec_tunnel_protect
1558 for i in range(existing_tunnels, n_tunnels):
1559 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1560 args[u"tunnel"][u"sa_out"] = i
1561 args[u"tunnel"][u"sa_in"] = [100000 + i]
1563 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1565 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1566 f"on host {nodes[u'DUT1'][u'host']}"
1567 papi_exec.get_replies(err_msg)
1569 # Configure unnumbered interfaces
1570 cmd = u"sw_interface_set_unnumbered"
1573 sw_if_index=InterfaceUtil.get_interface_index(
1574 nodes[u"DUT1"], if1_key
1576 unnumbered_sw_if_index=0
1578 for i in range(existing_tunnels, n_tunnels):
1579 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1581 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1584 cmd = u"sw_interface_set_flags"
1587 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1589 for i in range(existing_tunnels, n_tunnels):
1590 args[u"sw_if_index"] = ipip_tunnels[i]
1592 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1594 # Configure IP routes
1595 cmd = u"ip_route_add_del"
1601 for i in range(existing_tunnels, n_tunnels):
1602 args[u"route"] = IPUtil.compose_vpp_route_structure(
1603 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1604 prefix_len=128 if raddr_ip2.version == 6 else 32,
1605 interface=ipip_tunnels[i]
1608 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1610 err_msg = f"Failed to add IP routes on host " \
1611 f"{nodes[u'DUT1'][u'host']}"
1612 papi_exec.get_replies(err_msg)
1617 def _ipsec_create_tunnel_interfaces_dut2_papi(
1618 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1619 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1620 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1622 This method accesses keys generated by DUT1 method
1623 and does not return anything.
1625 :param nodes: VPP nodes to create tunnel interfaces.
1626 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1627 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1628 IPv4/IPv6 address (ip2).
1629 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1630 interface key from topology file.
1631 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1632 :param crypto_alg: The encryption algorithm name.
1633 :param ckeys: List of encryption keys.
1634 :param integ_alg: The integrity algorithm name.
1635 :param ikeys: List of integrity keys.
1636 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1637 :param addr_incr: IP / IPv6 address incremental step.
1638 :param existing_tunnels: Number of tunnel interfaces before creation.
1639 Useful mainly for reconf tests. Default 0.
1643 :type n_tunnels: int
1644 :type crypto_alg: CryptoAlg
1645 :type ckeys: Sequence[bytes]
1646 :type integ_alg: Optional[IntegAlg]
1647 :type ikeys: Sequence[bytes]
1648 :type addr_incr: int
1650 :type existing_tunnels: int
1652 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1653 if not existing_tunnels:
1654 # Set IP address on VPP node 2 interface
1655 cmd = u"sw_interface_add_del_address"
1657 sw_if_index=InterfaceUtil.get_interface_index(
1658 nodes[u"DUT2"], if2_key
1662 prefix=IPUtil.create_prefix_object(
1663 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1667 err_msg = f"Failed to set IP address on interface {if2_key} " \
1668 f"on host {nodes[u'DUT2'][u'host']}"
1669 papi_exec.add(cmd, **args).get_reply(err_msg)
1670 # Configure IPIP tunnel interfaces
1671 cmd = u"ipip_add_tunnel"
1673 instance=Constants.BITWISE_NON_ZERO,
1678 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1680 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1681 dscp=int(IpDscp.IP_API_DSCP_CS0)
1686 ipip_tunnels = [None] * existing_tunnels
1687 for i in range(existing_tunnels, n_tunnels):
1688 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1691 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1692 tun_ips[u"ip1"] + i * addr_incr
1695 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1697 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1698 f" {nodes[u'DUT2'][u'host']}"
1699 ipip_tunnels.extend(
1701 reply[u"sw_if_index"]
1702 for reply in papi_exec.get_replies(err_msg)
1703 if u"sw_if_index" in reply
1706 # Configure IPSec SAD entries
1707 cmd = u"ipsec_sad_entry_add"
1719 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1720 crypto_algorithm=crypto_alg.alg_int_repr,
1722 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1723 integrity_key=i_key,
1729 encap_decap_flags=int(
1730 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1732 dscp=int(IpDscp.IP_API_DSCP_CS0),
1735 udp_src_port=IPSEC_UDP_PORT_NONE,
1736 udp_dst_port=IPSEC_UDP_PORT_NONE,
1738 args = dict(entry=sad_entry)
1739 for i in range(existing_tunnels, n_tunnels):
1741 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1744 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1746 # SAD entry for outband / tx path
1747 args[u"entry"][u"sad_id"] = 100000 + i
1748 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1750 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1751 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1753 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1754 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1755 args[u"entry"][u"flags"] = int(
1756 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1759 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1761 # SAD entry for inband / rx path
1762 args[u"entry"][u"sad_id"] = i
1763 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1765 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1766 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1768 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1769 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1770 args[u"entry"][u"flags"] = int(
1771 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1772 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1775 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1777 err_msg = f"Failed to add IPsec SAD entries on host" \
1778 f" {nodes[u'DUT2'][u'host']}"
1779 papi_exec.get_replies(err_msg)
1780 # Add protection for tunnels with IPSEC
1781 cmd = u"ipsec_tunnel_protect_update"
1784 via_label=MPLS_LABEL_INVALID,
1785 obj_id=Constants.BITWISE_NON_ZERO
1787 ipsec_tunnel_protect = dict(
1795 tunnel=ipsec_tunnel_protect
1797 for i in range(existing_tunnels, n_tunnels):
1798 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1799 args[u"tunnel"][u"sa_out"] = 100000 + i
1800 args[u"tunnel"][u"sa_in"] = [i]
1802 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1804 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1805 f"on host {nodes[u'DUT2'][u'host']}"
1806 papi_exec.get_replies(err_msg)
1808 if not existing_tunnels:
1809 # Configure IP route
1810 cmd = u"ip_route_add_del"
1811 route = IPUtil.compose_vpp_route_structure(
1812 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1813 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1815 gateway=(tun_ips[u"ip2"] - 1).compressed
1822 papi_exec.add(cmd, **args)
1823 # Configure unnumbered interfaces
1824 cmd = u"sw_interface_set_unnumbered"
1827 sw_if_index=InterfaceUtil.get_interface_index(
1828 nodes[u"DUT2"], if2_key
1830 unnumbered_sw_if_index=0
1832 for i in range(existing_tunnels, n_tunnels):
1833 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1835 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1838 cmd = u"sw_interface_set_flags"
1841 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1843 for i in range(existing_tunnels, n_tunnels):
1844 args[u"sw_if_index"] = ipip_tunnels[i]
1846 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1848 # Configure IP routes
1849 cmd = u"ip_route_add_del"
1855 for i in range(existing_tunnels, n_tunnels):
1856 args[u"route"] = IPUtil.compose_vpp_route_structure(
1857 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1858 prefix_len=128 if raddr_ip1.version == 6 else 32,
1859 interface=ipip_tunnels[i]
1862 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1864 err_msg = f"Failed to add IP routes " \
1865 f"on host {nodes[u'DUT2'][u'host']}"
1866 papi_exec.get_replies(err_msg)
1869 def vpp_ipsec_create_tunnel_interfaces(
1870 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1871 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1872 existing_tunnels=0, return_keys=False):
1873 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1875 Some deployments (e.g. devicetest) need to know the generated keys.
1876 But other deployments (e.g. scale perf test) would get spammed
1877 if we returned keys every time.
1879 :param nodes: VPP nodes to create tunnel interfaces.
1880 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1882 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1884 :param if1_key: VPP node 1 interface key from topology file.
1885 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1886 interface key from topology file.
1887 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1888 :param crypto_alg: The encryption algorithm name.
1889 :param integ_alg: The integrity algorithm name.
1890 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1891 first tunnel in direction node1->node2.
1892 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1893 first tunnel in direction node2->node1.
1894 :param raddr_range: Mask specifying range of Policy selector Remote
1895 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1896 and to 128 in case of IPv6.
1897 :param existing_tunnels: Number of tunnel interfaces before creation.
1898 Useful mainly for reconf tests. Default 0.
1899 :param return_keys: Whether generated keys should be returned.
1901 :type tun_if1_ip_addr: str
1902 :type tun_if2_ip_addr: str
1905 :type n_tunnels: int
1906 :type crypto_alg: CryptoAlg
1907 :type integ_alg: Optonal[IntegAlg]
1908 :type raddr_ip1: string
1909 :type raddr_ip2: string
1910 :type raddr_range: int
1911 :type existing_tunnels: int
1912 :type return_keys: bool
1913 :returns: Ckeys, ikeys, spi_1, spi_2.
1914 :rtype: Optional[List[bytes], List[bytes], int, int]
1916 n_tunnels = int(n_tunnels)
1917 existing_tunnels = int(existing_tunnels)
1923 ip1=ip_address(tun_if1_ip_addr),
1924 ip2=ip_address(tun_if2_ip_addr)
1926 raddr_ip1 = ip_address(raddr_ip1)
1927 raddr_ip2 = ip_address(raddr_ip2)
1928 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1929 else 1 << (32 - raddr_range)
1931 if n_tunnels - existing_tunnels > 10:
1932 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1933 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1934 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1936 if u"DUT2" in nodes.keys():
1937 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1938 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1939 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1943 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1944 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1945 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1947 if u"DUT2" in nodes.keys():
1948 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1949 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1950 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1955 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1959 def _create_ipsec_script_files(dut, instances):
1960 """Create script files for configuring IPsec in containers
1962 :param dut: DUT node on which to create the script files
1963 :param instances: number of containers on DUT node
1965 :type instances: int
1968 for cnf in range(0, instances):
1970 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1972 scripts.append(open(script_filename, 'w'))
1976 def _close_and_copy_ipsec_script_files(
1977 dut, nodes, instances, scripts):
1978 """Close created scripts and copy them to containers
1980 :param dut: DUT node on which to create the script files
1981 :param nodes: VPP nodes
1982 :param instances: number of containers on DUT node
1983 :param scripts: dictionary holding the script files
1986 :type instances: int
1989 for cnf in range(0, instances):
1990 scripts[cnf].close()
1992 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1994 scp_node(nodes[dut], script_filename, script_filename)
1998 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1999 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
2000 raddr_ip1, raddr_ip2, raddr_range, n_instances):
2001 """Create multiple IPsec tunnel interfaces between two VPP nodes.
2003 :param nodes: VPP nodes to create tunnel interfaces.
2004 :param if1_ip_addr: VPP node 1 interface IP4 address.
2005 :param if2_ip_addr: VPP node 2 interface IP4 address.
2006 :param n_tunnels: Number of tunnell interfaces to create.
2007 :param crypto_alg: The encryption algorithm name.
2008 :param integ_alg: The integrity algorithm name.
2009 :param raddr_ip1: Policy selector remote IPv4 start address for the
2010 first tunnel in direction node1->node2.
2011 :param raddr_ip2: Policy selector remote IPv4 start address for the
2012 first tunnel in direction node2->node1.
2013 :param raddr_range: Mask specifying range of Policy selector Remote
2014 IPv4 addresses. Valid values are from 1 to 32.
2015 :param n_instances: Number of containers.
2017 :type if1_ip_addr: str
2018 :type if2_ip_addr: str
2019 :type n_tunnels: int
2020 :type crypto_alg: CryptoAlg
2021 :type integ_alg: Optional[IntegAlg]
2022 :type raddr_ip1: string
2023 :type raddr_ip2: string
2024 :type raddr_range: int
2025 :type n_instances: int
2029 addr_incr = 1 << (32 - raddr_range)
2031 dut1_scripts = IPsecUtil._create_ipsec_script_files(
2032 u"DUT1", n_instances
2034 dut2_scripts = IPsecUtil._create_ipsec_script_files(
2035 u"DUT2", n_instances
2038 for cnf in range(0, n_instances):
2039 dut1_scripts[cnf].write(
2040 u"create loopback interface\n"
2041 u"set interface state loop0 up\n\n"
2043 dut2_scripts[cnf].write(
2044 f"ip route add {if1_ip_addr}/8 via "
2045 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
2048 for tnl in range(0, n_tunnels):
2049 cnf = tnl % n_instances
2051 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
2055 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
2059 f"integ-alg {integ_alg.alg_name} "
2060 f"local-integ-key {ikey} "
2061 f"remote-integ-key {ikey} "
2063 # Configure tunnel end point(s) on left side
2064 dut1_scripts[cnf].write(
2065 u"set interface ip address loop0 "
2066 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
2067 f"create ipsec tunnel "
2068 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2069 f"local-spi {spi_1 + tnl} "
2070 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
2071 f"remote-spi {spi_2 + tnl} "
2072 f"crypto-alg {crypto_alg.alg_name} "
2073 f"local-crypto-key {ckey} "
2074 f"remote-crypto-key {ckey} "
2075 f"instance {tnl // n_instances} "
2078 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
2079 f"set interface state ipip{tnl // n_instances} up\n"
2080 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
2081 f"via ipip{tnl // n_instances}\n\n"
2083 # Configure tunnel end point(s) on right side
2084 dut2_scripts[cnf].write(
2085 f"set ip neighbor memif1/{cnf + 1} "
2086 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
2087 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
2088 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
2089 f"local-spi {spi_2 + tnl} "
2090 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2091 f"remote-spi {spi_1 + tnl} "
2092 f"crypto-alg {crypto_alg.alg_name} "
2093 f"local-crypto-key {ckey} "
2094 f"remote-crypto-key {ckey} "
2095 f"instance {tnl // n_instances} "
2098 f"set interface unnumbered ipip{tnl // n_instances} "
2099 f"use memif1/{cnf + 1}\n"
2100 f"set interface state ipip{tnl // n_instances} up\n"
2101 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
2102 f"via ipip{tnl // n_instances}\n\n"
2105 IPsecUtil._close_and_copy_ipsec_script_files(
2106 u"DUT1", nodes, n_instances, dut1_scripts)
2107 IPsecUtil._close_and_copy_ipsec_script_files(
2108 u"DUT2", nodes, n_instances, dut2_scripts)
2111 def vpp_ipsec_add_multiple_tunnels(
2112 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
2113 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range,
2114 tunnel_addr_incr=True):
2115 """Create multiple IPsec tunnels between two VPP nodes.
2117 :param nodes: VPP nodes to create tunnels.
2118 :param interface1: Interface name or sw_if_index on node 1.
2119 :param interface2: Interface name or sw_if_index on node 2.
2120 :param n_tunnels: Number of tunnels to create.
2121 :param crypto_alg: The encryption algorithm name.
2122 :param integ_alg: The integrity algorithm name.
2123 :param tunnel_ip1: Tunnel node1 IPv4 address.
2124 :param tunnel_ip2: Tunnel node2 IPv4 address.
2125 :param raddr_ip1: Policy selector remote IPv4 start address for the
2126 first tunnel in direction node1->node2.
2127 :param raddr_ip2: Policy selector remote IPv4 start address for the
2128 first tunnel in direction node2->node1.
2129 :param raddr_range: Mask specifying range of Policy selector Remote
2130 IPv4 addresses. Valid values are from 1 to 32.
2131 :param tunnel_addr_incr: Enable or disable tunnel IP address
2134 :type interface1: str or int
2135 :type interface2: str or int
2136 :type n_tunnels: int
2137 :type crypto_alg: CryptoAlg
2138 :type integ_alg: Optional[IntegAlg]
2139 :type tunnel_ip1: str
2140 :type tunnel_ip2: str
2141 :type raddr_ip1: string
2142 :type raddr_ip2: string
2143 :type raddr_range: int
2144 :type tunnel_addr_incr: bool
2153 dut1_local_outbound_range = ip_network(f"{tunnel_ip1}/8", False).\
2155 dut1_remote_outbound_range = ip_network(f"{tunnel_ip2}/8", False).\
2158 crypto_key = gen_key(
2159 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2161 integ_key = gen_key(
2162 IPsecUtil.get_integ_alg_key_len(integ_alg)
2163 ).decode() if integ_alg else u""
2165 rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
2166 if u"DUT2" in nodes.keys() \
2167 else Topology.get_interface_mac(nodes[u"TG"], interface2)
2168 IPsecUtil.vpp_ipsec_set_ip_route(
2169 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
2170 interface1, raddr_range, rmac)
2172 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
2173 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
2174 IPsecUtil.vpp_ipsec_add_spd_entry(
2175 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
2176 proto=50, laddr_range=dut1_local_outbound_range,
2177 raddr_range=dut1_remote_outbound_range
2179 IPsecUtil.vpp_ipsec_add_spd_entry(
2180 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
2181 proto=50, laddr_range=dut1_remote_outbound_range,
2182 raddr_range=dut1_local_outbound_range
2185 IPsecUtil.vpp_ipsec_add_sad_entries(
2186 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
2187 integ_alg, integ_key, tunnel_ip1, tunnel_ip2, tunnel_addr_incr
2190 IPsecUtil.vpp_ipsec_add_spd_entries(
2191 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2192 action=PolicyAction.PROTECT, inbound=False,
2193 sa_id=ObjIncrement(sa_id_1, 1),
2194 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2197 IPsecUtil.vpp_ipsec_add_sad_entries(
2198 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
2199 integ_alg, integ_key, tunnel_ip2, tunnel_ip1, tunnel_addr_incr
2201 IPsecUtil.vpp_ipsec_add_spd_entries(
2202 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2203 action=PolicyAction.PROTECT, inbound=True,
2204 sa_id=ObjIncrement(sa_id_2, 1),
2205 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2208 if u"DUT2" in nodes.keys():
2209 rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
2210 IPsecUtil.vpp_ipsec_set_ip_route(
2211 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2212 interface2, raddr_range, rmac)
2214 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2215 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2216 IPsecUtil.vpp_ipsec_add_spd_entry(
2217 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2218 inbound=False, proto=50, laddr_range=dut1_remote_outbound_range,
2219 raddr_range=dut1_local_outbound_range
2221 IPsecUtil.vpp_ipsec_add_spd_entry(
2222 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2223 inbound=True, proto=50, laddr_range=dut1_local_outbound_range,
2224 raddr_range=dut1_remote_outbound_range
2227 IPsecUtil.vpp_ipsec_add_sad_entries(
2228 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2229 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2,
2232 IPsecUtil.vpp_ipsec_add_spd_entries(
2233 nodes[u"DUT2"], n_tunnels, spd_id,
2234 priority=ObjIncrement(p_lo, 0),
2235 action=PolicyAction.PROTECT, inbound=True,
2236 sa_id=ObjIncrement(sa_id_1, 1),
2237 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2240 IPsecUtil.vpp_ipsec_add_sad_entries(
2241 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2242 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1,
2245 IPsecUtil.vpp_ipsec_add_spd_entries(
2246 nodes[u"DUT2"], n_tunnels, spd_id,
2247 priority=ObjIncrement(p_lo, 0),
2248 action=PolicyAction.PROTECT, inbound=False,
2249 sa_id=ObjIncrement(sa_id_2, 1),
2250 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2254 def vpp_ipsec_show_all(node):
2255 """Run "show ipsec all" debug CLI command.
2257 :param node: Node to run command on.
2260 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2263 def show_ipsec_security_association(node):
2264 """Show IPSec security association.
2266 :param node: DUT node.
2272 PapiSocketExecutor.dump_and_log(node, cmds)
2275 def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2276 """Ipsec flow enable rss action.
2278 :param node: DUT node.
2279 :param proto: The flow protocol.
2280 :param type: RSS type.
2281 :param function: RSS function.
2287 :returns: flow_index.
2289 # TODO: to be fixed to use full PAPI when it is ready in VPP
2290 cmd = f"test flow add src-ip any proto {proto} rss function " \
2291 f"{function} rss types {type}"
2292 stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2293 flow_index = stdout.split()[1]
2298 def vpp_create_ipsec_flows_on_dut(
2299 node, n_flows, rx_queues, spi_start, interface):
2300 """Create mutiple ipsec flows and enable flows onto interface.
2302 :param node: DUT node.
2303 :param n_flows: Number of flows to create.
2304 :param rx_queues: NUmber of RX queues.
2305 :param spi_start: The start spi.
2306 :param interface: Name of the interface.
2310 :type rx_queues: int
2311 :type spi_start: int
2312 :type interface: str
2313 :returns: flow_index.
2316 for i in range(0, n_flows):
2317 rx_queue = i%rx_queues
2320 flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2321 node, "ESP", spi, "redirect-to-queue", value=rx_queue)
2322 FlowUtil.vpp_flow_enable(node, interface, flow_index)