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_del_v3"
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
469 with PapiSocketExecutor(node) as papi_exec:
470 papi_exec.add(cmd, **args).get_reply(err_msg)
473 def vpp_ipsec_add_sad_entries(
474 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
475 integ_alg=None, integ_key=u"", tunnel_src=None,tunnel_dst=None,
476 tunnel_addr_incr=True):
477 """Create multiple Security Association Database entries on VPP node.
479 :param node: VPP node to add SAD entry on.
480 :param n_entries: Number of SAD entries to be created.
481 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
483 :param spi: Security Parameter Index of first SAD entry. All subsequent
484 SAD entries will have spi incremented by 1.
485 :param crypto_alg: The encryption algorithm name.
486 :param crypto_key: The encryption key string.
487 :param integ_alg: The integrity algorithm name.
488 :param integ_key: The integrity key string.
489 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
490 specified ESP transport mode is used.
491 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
492 not specified ESP transport mode is used.
493 :param tunnel_addr_incr: Enable or disable tunnel IP address
499 :type crypto_alg: CryptoAlg
500 :type crypto_key: str
501 :type integ_alg: Optional[IntegAlg]
503 :type tunnel_src: str
504 :type tunnel_dst: str
505 :type tunnel_addr_incr: bool
507 if isinstance(crypto_key, str):
508 crypto_key = crypto_key.encode(encoding=u"utf-8")
509 if isinstance(integ_key, str):
510 integ_key = integ_key.encode(encoding=u"utf-8")
511 if tunnel_src and tunnel_dst:
512 src_addr = ip_address(tunnel_src)
513 dst_addr = ip_address(tunnel_dst)
519 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
524 if int(n_entries) > 10:
525 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
527 with open(tmp_filename, 'w') as tmp_file:
528 for i in range(n_entries):
529 integ = f"integ-alg {integ_alg.alg_name} " \
530 f"integ-key {integ_key.hex()}" \
531 if integ_alg else u""
532 tunnel = f"tunnel src {src_addr + i * addr_incr} " \
533 f"tunnel dst {dst_addr + i * addr_incr}" \
534 if tunnel_src and tunnel_dst else u""
535 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
536 f"crypto-alg {crypto_alg.alg_name} " \
537 f"crypto-key {crypto_key.hex()} " \
538 f"{integ} {tunnel}\n"
542 tmp_filename, node, timeout=300, json_out=False,
545 os.remove(tmp_filename)
549 length=len(crypto_key),
553 length=len(integ_key),
554 data=integ_key if integ_key else 0
557 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
558 if tunnel_src and tunnel_dst:
559 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
560 if src_addr.version == 6:
562 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
565 cmd = u"ipsec_sad_entry_add_del_v3"
566 err_msg = f"Failed to add Security Association Database entry " \
567 f"on host {node[u'host']}"
572 crypto_algorithm=crypto_alg.alg_int_repr,
574 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
581 encap_decap_flags=int(
582 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
584 dscp=int(IpDscp.IP_API_DSCP_CS0),
586 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
587 udp_src_port=4500, # default value in api
588 udp_dst_port=4500 # default value in api
594 with PapiSocketExecutor(node) as papi_exec:
595 for i in range(n_entries):
596 args[u"entry"][u"sad_id"] = int(sad_id) + i
597 args[u"entry"][u"spi"] = int(spi) + i
598 args[u"entry"][u"tunnel"][u"src"] = (
599 str(src_addr + i * addr_incr)
600 if tunnel_src and tunnel_dst else src_addr
602 args[u"entry"][u"tunnel"][u"dst"] = (
603 str(dst_addr + i * addr_incr)
604 if tunnel_src and tunnel_dst else dst_addr
606 history = bool(not 1 < i < n_entries - 2)
607 papi_exec.add(cmd, history=history, **args)
608 papi_exec.get_replies(err_msg)
611 def vpp_ipsec_set_ip_route(
612 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
613 raddr_range, dst_mac=None):
614 """Set IP address and route on interface.
616 :param node: VPP node to add config on.
617 :param n_tunnels: Number of tunnels to create.
618 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
619 :param traffic_addr: Traffic destination IP address to route.
620 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
621 :param interface: Interface key on node 1.
622 :param raddr_range: Mask specifying range of Policy selector Remote IP
623 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
625 :param dst_mac: The MAC address of destination tunnels.
628 :type tunnel_src: str
629 :type traffic_addr: str
630 :type tunnel_dst: str
632 :type raddr_range: int
635 tunnel_src = ip_address(tunnel_src)
636 tunnel_dst = ip_address(tunnel_dst)
637 traffic_addr = ip_address(traffic_addr)
638 tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
639 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
640 else 1 << (32 - raddr_range)
642 if int(n_tunnels) > 10:
643 tmp_filename = u"/tmp/ipsec_set_ip.script"
645 with open(tmp_filename, 'w') as tmp_file:
646 if_name = Topology.get_interface_name(node, interface)
647 for i in range(n_tunnels):
648 tunnel_dst_addr = tunnel_dst + i * addr_incr
649 conf = f"exec set interface ip address {if_name} " \
650 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
651 f"exec ip route add {traffic_addr + i}/" \
652 f"{tunnel_dst_prefix} " \
653 f"via {tunnel_dst_addr} {if_name}\n" \
654 f"exec ip route add {tunnel_dst_addr}/" \
655 f"{tunnel_dst_prefix} " \
656 f"via {tunnel_dst_addr} {if_name}\n"
658 conf = f"{conf}exec set ip neighbor {if_name} " \
659 f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
662 VatExecutor().execute_script(
663 tmp_filename, node, timeout=300, json_out=False,
666 os.remove(tmp_filename)
669 cmd1 = u"sw_interface_add_del_address"
671 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
676 cmd2 = u"ip_route_add_del"
682 cmd3 = u"ip_neighbor_add_del"
686 sw_if_index=Topology.get_interface_sw_index(node, interface),
688 mac_address=str(dst_mac),
692 err_msg = f"Failed to configure IP addresses, IP routes and " \
693 f"IP neighbor on interface {interface} on host {node[u'host']}" \
695 else f"Failed to configure IP addresses and IP routes " \
696 f"on interface {interface} on host {node[u'host']}"
698 with PapiSocketExecutor(node) as papi_exec:
699 for i in range(n_tunnels):
700 tunnel_dst_addr = tunnel_dst + i * addr_incr
701 args1[u"prefix"] = IPUtil.create_prefix_object(
702 tunnel_src + i * addr_incr, raddr_range
704 args2[u"route"] = IPUtil.compose_vpp_route_structure(
705 node, traffic_addr + i,
706 prefix_len=tunnel_dst_prefix,
707 interface=interface, gateway=tunnel_dst_addr
709 history = bool(not 1 < i < n_tunnels - 2)
710 papi_exec.add(cmd1, history=history, **args1).\
711 add(cmd2, history=history, **args2)
713 args2[u"route"] = IPUtil.compose_vpp_route_structure(
714 node, tunnel_dst_addr,
715 prefix_len=tunnel_dst_prefix,
716 interface=interface, gateway=tunnel_dst_addr
718 papi_exec.add(cmd2, history=history, **args2)
721 args3[u"neighbor"][u"ip_address"] = ip_address(
724 papi_exec.add(cmd3, history=history, **args3)
725 papi_exec.get_replies(err_msg)
728 def vpp_ipsec_add_spd(node, spd_id):
729 """Create Security Policy Database on the VPP node.
731 :param node: VPP node to add SPD on.
732 :param spd_id: SPD ID.
736 cmd = u"ipsec_spd_add_del"
737 err_msg = f"Failed to add Security Policy Database " \
738 f"on host {node[u'host']}"
743 with PapiSocketExecutor(node) as papi_exec:
744 papi_exec.add(cmd, **args).get_reply(err_msg)
747 def vpp_ipsec_spd_add_if(node, spd_id, interface):
748 """Add interface to the Security Policy Database.
750 :param node: VPP node.
751 :param spd_id: SPD ID to add interface on.
752 :param interface: Interface name or sw_if_index.
755 :type interface: str or int
757 cmd = u"ipsec_interface_add_del_spd"
758 err_msg = f"Failed to add interface {interface} to Security Policy " \
759 f"Database {spd_id} on host {node[u'host']}"
762 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
765 with PapiSocketExecutor(node) as papi_exec:
766 papi_exec.add(cmd, **args).get_reply(err_msg)
769 def vpp_ipsec_create_spds_match_nth_entry(
770 node, dir1_interface, dir2_interface, entry_amount,
771 local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
772 inbound=False, bidirectional=True):
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(
826 node, spd_id_dir1, matching_priority, action,
827 inbound=inbound, laddr_range=local_addr_range,
828 raddr_range=remote_addr_range
832 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
833 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
835 # matching entry direction 2, the address ranges are switched
836 IPsecUtil.vpp_ipsec_add_spd_entry(
837 node, spd_id_dir2, matching_priority, action,
838 inbound=inbound, laddr_range=remote_addr_range,
839 raddr_range=local_addr_range
842 # non-matching entries
843 no_match_entry_amount = entry_amount - 1
844 if no_match_entry_amount > 0:
845 # create a NetworkIncrement representation of the network,
846 # then skip the matching network
847 no_match_local_addr_range = NetworkIncrement(
848 ip_network(local_addr_range)
850 next(no_match_local_addr_range)
852 no_match_remote_addr_range = NetworkIncrement(
853 ip_network(remote_addr_range)
855 next(no_match_remote_addr_range)
857 # non-matching entries direction 1
858 IPsecUtil.vpp_ipsec_add_spd_entries(
859 node, no_match_entry_amount, spd_id_dir1,
860 ObjIncrement(matching_priority + 1, 1), action,
861 inbound=inbound, laddr_range=no_match_local_addr_range,
862 raddr_range=no_match_remote_addr_range
866 # reset the networks so that we're using a unified config
867 # the address ranges are switched
868 no_match_remote_addr_range = NetworkIncrement(
869 ip_network(local_addr_range)
871 next(no_match_remote_addr_range)
873 no_match_local_addr_range = NetworkIncrement(
874 ip_network(remote_addr_range)
876 next(no_match_local_addr_range)
877 # non-matching entries direction 2
878 IPsecUtil.vpp_ipsec_add_spd_entries(
879 node, no_match_entry_amount, spd_id_dir2,
880 ObjIncrement(matching_priority + 1, 1), action,
881 inbound=inbound, laddr_range=no_match_local_addr_range,
882 raddr_range=no_match_remote_addr_range
885 IPsecUtil.vpp_ipsec_show_all(node)
888 def vpp_ipsec_add_spd_entry(
889 node, spd_id, priority, action, inbound=True, sa_id=None,
890 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
891 rport_range=None, is_ipv6=False):
892 """Create Security Policy Database entry on the VPP node.
894 :param node: VPP node to add SPD entry on.
895 :param spd_id: SPD ID to add entry on.
896 :param priority: SPD entry priority, higher number = higher priority.
897 :param action: Policy action.
898 :param inbound: If True policy is for inbound traffic, otherwise
900 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
901 :param proto: Policy selector next layer protocol number.
902 :param laddr_range: Policy selector local IPv4 or IPv6 address range
903 in format IP/prefix or IP/mask. If no mask is provided,
904 it's considered to be /32.
905 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
906 in format IP/prefix or IP/mask. If no mask is provided,
907 it's considered to be /32.
908 :param lport_range: Policy selector local TCP/UDP port range in format
909 <port_start>-<port_end>.
910 :param rport_range: Policy selector remote TCP/UDP port range in format
911 <port_start>-<port_end>.
912 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
913 not defined so it will default to address ::/0, otherwise False.
917 :type action: IPsecUtil.PolicyAction
921 :type laddr_range: string
922 :type raddr_range: string
923 :type lport_range: string
924 :type rport_range: string
927 if laddr_range is None:
928 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
930 if raddr_range is None:
931 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
933 local_net = ip_network(laddr_range, strict=False)
934 remote_net = ip_network(raddr_range, strict=False)
936 cmd = u"ipsec_spd_entry_add_del"
937 err_msg = f"Failed to add entry to Security Policy Database " \
938 f"{spd_id} on host {node[u'host']}"
942 priority=int(priority),
943 is_outbound=not inbound,
944 sa_id=int(sa_id) if sa_id else 0,
946 protocol=int(proto) if proto else 0,
947 remote_address_start=IPAddress.create_ip_address_object(
948 remote_net.network_address
950 remote_address_stop=IPAddress.create_ip_address_object(
951 remote_net.broadcast_address
953 local_address_start=IPAddress.create_ip_address_object(
954 local_net.network_address
956 local_address_stop=IPAddress.create_ip_address_object(
957 local_net.broadcast_address
959 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
961 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
963 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
965 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
972 with PapiSocketExecutor(node) as papi_exec:
973 papi_exec.add(cmd, **args).get_reply(err_msg)
976 def vpp_ipsec_add_spd_entries(
977 node, n_entries, spd_id, priority, action, inbound, sa_id=None,
978 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
979 rport_range=None, is_ipv6=False):
980 """Create multiple Security Policy Database entries on the VPP node.
982 :param node: VPP node to add SPD entries on.
983 :param n_entries: Number of SPD entries to be added.
984 :param spd_id: SPD ID to add entries on.
985 :param priority: SPD entries priority, higher number = higher priority.
986 :param action: Policy action.
987 :param inbound: If True policy is for inbound traffic, otherwise
989 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
990 :param proto: Policy selector next layer protocol number.
991 :param laddr_range: Policy selector local IPv4 or IPv6 address range
992 in format IP/prefix or IP/mask. If no mask is provided,
993 it's considered to be /32.
994 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
995 in format IP/prefix or IP/mask. If no mask is provided,
996 it's considered to be /32.
997 :param lport_range: Policy selector local TCP/UDP port range in format
998 <port_start>-<port_end>.
999 :param rport_range: Policy selector remote TCP/UDP port range in format
1000 <port_start>-<port_end>.
1001 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
1002 not defined so it will default to address ::/0, otherwise False.
1004 :type n_entries: int
1006 :type priority: IPsecUtil.ObjIncrement
1007 :type action: IPsecUtil.PolicyAction
1009 :type sa_id: IPsecUtil.ObjIncrement
1011 :type laddr_range: IPsecUtil.NetworkIncrement
1012 :type raddr_range: IPsecUtil.NetworkIncrement
1013 :type lport_range: string
1014 :type rport_range: string
1017 if laddr_range is None:
1018 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1019 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1021 if raddr_range is None:
1022 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1023 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1025 lport_range_start = 0
1026 lport_range_stop = 65535
1028 lport_range_start, lport_range_stop = lport_range.split('-')
1030 rport_range_start = 0
1031 rport_range_stop = 65535
1033 rport_range_start, rport_range_stop = rport_range.split('-')
1035 if int(n_entries) > 10:
1036 tmp_filename = f"/tmp/ipsec_spd_{spd_id}_add_del_entry.script"
1038 with open(tmp_filename, 'w') as tmp_file:
1039 for _ in range(n_entries):
1040 direction = u'inbound' if inbound else u'outbound'
1041 sa = f' sa {sa_id.inc_fmt()}' if sa_id is not None else ''
1042 protocol = f' protocol {protocol}' if proto else ''
1043 local_port_range = f' local-port-range ' \
1044 f'{lport_range_start} - {lport_range_stop}' \
1045 if lport_range else ''
1046 remote_port_range = f' remote-port-range ' \
1047 f'{rport_range_start} - {rport_range_stop}' \
1048 if rport_range else ''
1050 spd_cfg = f"exec ipsec policy add spd {spd_id} " \
1051 f"priority {priority.inc_fmt()} {direction}" \
1052 f"{protocol} action {action}{sa} " \
1053 f"local-ip-range {laddr_range.inc_fmt()} " \
1054 f"remote-ip-range {raddr_range.inc_fmt()}" \
1055 f"{local_port_range}{remote_port_range}\n"
1057 tmp_file.write(spd_cfg)
1059 VatExecutor().execute_script(
1060 tmp_filename, node, timeout=300, json_out=False,
1061 copy_on_execute=True
1063 os.remove(tmp_filename)
1066 for _ in range(n_entries):
1067 IPsecUtil.vpp_ipsec_add_spd_entry(
1068 node, spd_id, next(priority), action, inbound,
1069 next(sa_id) if sa_id is not None else sa_id,
1070 proto, next(laddr_range), next(raddr_range), lport_range,
1071 rport_range, is_ipv6
1075 def _ipsec_create_tunnel_interfaces_dut1_vat(
1076 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1077 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1078 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
1080 Generate random keys and return them (so DUT2 or TG can decrypt).
1082 :param nodes: VPP nodes to create tunnel interfaces.
1083 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1084 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1085 IPv4/IPv6 address (ip2).
1086 :param if1_key: VPP node 1 interface key from topology file.
1087 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1088 interface key from topology file.
1089 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1090 :param crypto_alg: The encryption algorithm name.
1091 :param integ_alg: The integrity algorithm name.
1092 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1093 first tunnel in direction node2->node1.
1094 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1095 :param addr_incr: IP / IPv6 address incremental step.
1096 :param existing_tunnels: Number of tunnel interfaces before creation.
1097 Useful mainly for reconf tests. Default 0.
1102 :type n_tunnels: int
1103 :type crypto_alg: CryptoAlg
1104 :type integ_alg: Optional[IntegAlg]
1105 :type raddr_ip2: IPv4Address or IPv6Address
1106 :type addr_incr: int
1108 :type existing_tunnels: int
1109 :returns: Generated ckeys and ikeys.
1110 :rtype: List[bytes], List[bytes]
1112 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
1113 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
1115 ckeys = [bytes()] * existing_tunnels
1116 ikeys = [bytes()] * existing_tunnels
1119 with open(tmp_fn1, u"w") as tmp_f1:
1120 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
1121 if u"DUT2" in nodes.keys() \
1122 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
1123 if not existing_tunnels:
1125 f"exec create loopback interface\n"
1126 f"exec set interface state loop0 up\n"
1127 f"exec set interface ip address {if1_n} "
1128 f"{tun_ips[u'ip2'] - 1}/"
1129 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
1130 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
1133 for i in range(existing_tunnels, n_tunnels):
1135 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1138 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1141 integ = f"integ-alg {integ_alg.alg_name} " \
1142 f"integ-key {ikeys[i].hex()} "
1146 f"exec set interface ip address loop0 "
1147 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
1148 f"exec create ipip tunnel "
1149 f"src {tun_ips[u'ip1'] + i * addr_incr} "
1150 f"dst {tun_ips[u'ip2']} "
1152 f"exec ipsec sa add {i} "
1153 f"spi {spi_d[u'spi_1'] + i} "
1154 f"crypto-alg {crypto_alg.alg_name} "
1155 f"crypto-key {ckeys[i].hex()} "
1158 f"exec ipsec sa add {100000 + i} "
1159 f"spi {spi_d[u'spi_2'] + i} "
1160 f"crypto-alg {crypto_alg.alg_name} "
1161 f"crypto-key {ckeys[i].hex()} "
1164 f"exec ipsec tunnel protect ipip{i} "
1166 f"sa-in {100000 + i} "
1170 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1171 copy_on_execute=True,
1172 history=bool(n_tunnels < 100)
1176 with open(tmp_fn1, 'w') as tmp_f1:
1177 for i in range(existing_tunnels, n_tunnels):
1179 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
1180 f"exec set interface state ipip{i} up\n"
1181 f"exec ip route add "
1182 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
1186 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1187 copy_on_execute=True,
1188 history=bool(n_tunnels < 100)
1195 def _ipsec_create_tunnel_interfaces_dut2_vat(
1196 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1197 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1198 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
1200 This method accesses keys generated by DUT1 method
1201 and does not return anything.
1203 :param nodes: VPP nodes to create tunnel interfaces.
1204 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1205 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1206 IPv4/IPv6 address (ip2).
1207 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1208 interface key from topology file.
1209 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1210 :param crypto_alg: The encryption algorithm name.
1211 :param ckeys: List of encryption keys.
1212 :param integ_alg: The integrity algorithm name.
1213 :param ikeys: List of integrity keys.
1214 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1215 :param addr_incr: IP / IPv6 address incremental step.
1216 :param existing_tunnels: Number of tunnel interfaces before creation.
1217 Useful mainly for reconf tests. Default 0.
1221 :type n_tunnels: int
1222 :type crypto_alg: CryptoAlg
1223 :type ckeys: Sequence[bytes]
1224 :type integ_alg: Optional[IntegAlg]
1225 :type ikeys: Sequence[bytes]
1226 :type addr_incr: int
1228 :type existing_tunnels: int
1230 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1231 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1234 with open(tmp_fn2, 'w') as tmp_f2:
1235 if not existing_tunnels:
1237 f"exec set interface ip address {if2_n}"
1238 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1240 for i in range(existing_tunnels, n_tunnels):
1242 integ = f"integ-alg {integ_alg.alg_name} " \
1243 f"integ-key {ikeys[i].hex()} "
1247 f"exec create ipip tunnel "
1248 f"src {tun_ips[u'ip2']} "
1249 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1251 f"exec ipsec sa add {100000 + i} "
1252 f"spi {spi_d[u'spi_2'] + i} "
1253 f"crypto-alg {crypto_alg.alg_name} "
1254 f"crypto-key {ckeys[i].hex()} "
1257 f"exec ipsec sa add {i} "
1258 f"spi {spi_d[u'spi_1'] + i} "
1259 f"crypto-alg {crypto_alg.alg_name} "
1260 f"crypto-key {ckeys[i].hex()} "
1263 f"exec ipsec tunnel protect ipip{i} "
1264 f"sa-out {100000 + i} "
1269 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1270 copy_on_execute=True,
1271 history=bool(n_tunnels < 100)
1275 with open(tmp_fn2, 'w') as tmp_f2:
1276 if not existing_tunnels:
1278 f"exec ip route add {tun_ips[u'ip1']}/8 "
1279 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1281 for i in range(existing_tunnels, n_tunnels):
1283 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1284 f"exec set interface state ipip{i} up\n"
1285 f"exec ip route add "
1286 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1290 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1291 copy_on_execute=True,
1292 history=bool(n_tunnels < 100)
1297 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1298 """Create loopback interface and set IP address on VPP node 1 interface
1301 :param nodes: VPP nodes to create tunnel interfaces.
1302 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1303 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1304 IPv4/IPv6 address (ip2).
1305 :param if1_key: VPP node 1 interface key from topology file.
1306 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1307 interface key from topology file.
1313 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1314 # Create loopback interface on DUT1, set it to up state
1315 cmd = u"create_loopback_instance"
1321 err_msg = f"Failed to create loopback interface " \
1322 f"on host {nodes[u'DUT1'][u'host']}"
1323 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1324 get_sw_if_index(err_msg)
1325 cmd = u"sw_interface_set_flags"
1327 sw_if_index=loop_sw_if_idx,
1328 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1330 err_msg = f"Failed to set loopback interface state up " \
1331 f"on host {nodes[u'DUT1'][u'host']}"
1332 papi_exec.add(cmd, **args).get_reply(err_msg)
1333 # Set IP address on VPP node 1 interface
1334 cmd = u"sw_interface_add_del_address"
1336 sw_if_index=InterfaceUtil.get_interface_index(
1337 nodes[u"DUT1"], if1_key
1341 prefix=IPUtil.create_prefix_object(
1342 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1346 err_msg = f"Failed to set IP address on interface {if1_key} " \
1347 f"on host {nodes[u'DUT1'][u'host']}"
1348 papi_exec.add(cmd, **args).get_reply(err_msg)
1349 cmd2 = u"ip_neighbor_add_del"
1353 sw_if_index=Topology.get_interface_sw_index(
1354 nodes[u"DUT1"], if1_key
1358 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1359 if u"DUT2" in nodes.keys()
1360 else Topology.get_interface_mac(
1361 nodes[u"TG"], if2_key
1364 ip_address=tun_ips[u"ip2"].compressed
1367 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1368 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1370 return loop_sw_if_idx
1373 def _ipsec_create_tunnel_interfaces_dut1_papi(
1374 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1375 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1376 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1378 Generate random keys and return them (so DUT2 or TG can decrypt).
1380 :param nodes: VPP nodes to create tunnel interfaces.
1381 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1382 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1383 IPv4/IPv6 address (ip2).
1384 :param if1_key: VPP node 1 interface key from topology file.
1385 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1386 interface key from topology file.
1387 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1388 :param crypto_alg: The encryption algorithm name.
1389 :param integ_alg: The integrity algorithm name.
1390 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1391 first tunnel in direction node2->node1.
1392 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1393 :param addr_incr: IP / IPv6 address incremental step.
1394 :param existing_tunnels: Number of tunnel interfaces before creation.
1395 Useful mainly for reconf tests. Default 0.
1400 :type n_tunnels: int
1401 :type crypto_alg: CryptoAlg
1402 :type integ_alg: Optional[IntegAlg]
1403 :type raddr_ip2: IPv4Address or IPv6Address
1404 :type addr_incr: int
1406 :type existing_tunnels: int
1407 :returns: Generated ckeys and ikeys.
1408 :rtype: List[bytes], List[bytes]
1410 if not existing_tunnels:
1411 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1412 nodes, tun_ips, if1_key, if2_key
1415 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1416 nodes[u"DUT1"], u"loop0"
1418 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1419 # Configure IP addresses on loop0 interface
1420 cmd = u"sw_interface_add_del_address"
1422 sw_if_index=loop_sw_if_idx,
1427 for i in range(existing_tunnels, n_tunnels):
1428 args[u"prefix"] = IPUtil.create_prefix_object(
1429 tun_ips[u"ip1"] + i * addr_incr,
1430 128 if tun_ips[u"ip1"].version == 6 else 32
1433 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1435 # Configure IPIP tunnel interfaces
1436 cmd = u"ipip_add_tunnel"
1438 instance=Constants.BITWISE_NON_ZERO,
1443 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1445 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1446 dscp=int(IpDscp.IP_API_DSCP_CS0)
1451 ipip_tunnels = [None] * existing_tunnels
1452 for i in range(existing_tunnels, n_tunnels):
1453 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1454 tun_ips[u"ip1"] + i * addr_incr
1456 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1460 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1462 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1463 f" {nodes[u'DUT1'][u'host']}"
1464 ipip_tunnels.extend(
1466 reply[u"sw_if_index"]
1467 for reply in papi_exec.get_replies(err_msg)
1468 if u"sw_if_index" in reply
1471 # Configure IPSec SAD entries
1472 ckeys = [bytes()] * existing_tunnels
1473 ikeys = [bytes()] * existing_tunnels
1474 cmd = u"ipsec_sad_entry_add_del_v3"
1486 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1487 crypto_algorithm=crypto_alg.alg_int_repr,
1489 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1490 integrity_key=i_key,
1496 encap_decap_flags=int(
1497 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1499 dscp=int(IpDscp.IP_API_DSCP_CS0),
1502 udp_src_port=IPSEC_UDP_PORT_NONE,
1503 udp_dst_port=IPSEC_UDP_PORT_NONE,
1509 for i in range(existing_tunnels, n_tunnels):
1511 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1514 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1516 # SAD entry for outband / tx path
1517 args[u"entry"][u"sad_id"] = i
1518 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1520 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1521 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1523 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1524 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1525 args[u"entry"][u"flags"] = int(
1526 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1529 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1531 # SAD entry for inband / rx path
1532 args[u"entry"][u"sad_id"] = 100000 + i
1533 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1535 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1536 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1538 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1539 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1540 args[u"entry"][u"flags"] = int(
1541 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1542 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1545 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1547 err_msg = f"Failed to add IPsec SAD entries on host" \
1548 f" {nodes[u'DUT1'][u'host']}"
1549 papi_exec.get_replies(err_msg)
1550 # Add protection for tunnels with IPSEC
1551 cmd = u"ipsec_tunnel_protect_update"
1554 via_label=MPLS_LABEL_INVALID,
1555 obj_id=Constants.BITWISE_NON_ZERO
1557 ipsec_tunnel_protect = dict(
1565 tunnel=ipsec_tunnel_protect
1567 for i in range(existing_tunnels, n_tunnels):
1568 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1569 args[u"tunnel"][u"sa_out"] = i
1570 args[u"tunnel"][u"sa_in"] = [100000 + i]
1572 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1574 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1575 f"on host {nodes[u'DUT1'][u'host']}"
1576 papi_exec.get_replies(err_msg)
1578 # Configure unnumbered interfaces
1579 cmd = u"sw_interface_set_unnumbered"
1582 sw_if_index=InterfaceUtil.get_interface_index(
1583 nodes[u"DUT1"], if1_key
1585 unnumbered_sw_if_index=0
1587 for i in range(existing_tunnels, n_tunnels):
1588 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1590 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1593 cmd = u"sw_interface_set_flags"
1596 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1598 for i in range(existing_tunnels, n_tunnels):
1599 args[u"sw_if_index"] = ipip_tunnels[i]
1601 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1603 # Configure IP routes
1604 cmd = u"ip_route_add_del"
1610 for i in range(existing_tunnels, n_tunnels):
1611 args[u"route"] = IPUtil.compose_vpp_route_structure(
1612 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1613 prefix_len=128 if raddr_ip2.version == 6 else 32,
1614 interface=ipip_tunnels[i]
1617 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1619 err_msg = f"Failed to add IP routes on host " \
1620 f"{nodes[u'DUT1'][u'host']}"
1621 papi_exec.get_replies(err_msg)
1626 def _ipsec_create_tunnel_interfaces_dut2_papi(
1627 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1628 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1629 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1631 This method accesses keys generated by DUT1 method
1632 and does not return anything.
1634 :param nodes: VPP nodes to create tunnel interfaces.
1635 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1636 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1637 IPv4/IPv6 address (ip2).
1638 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1639 interface key from topology file.
1640 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1641 :param crypto_alg: The encryption algorithm name.
1642 :param ckeys: List of encryption keys.
1643 :param integ_alg: The integrity algorithm name.
1644 :param ikeys: List of integrity keys.
1645 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1646 :param addr_incr: IP / IPv6 address incremental step.
1647 :param existing_tunnels: Number of tunnel interfaces before creation.
1648 Useful mainly for reconf tests. Default 0.
1652 :type n_tunnels: int
1653 :type crypto_alg: CryptoAlg
1654 :type ckeys: Sequence[bytes]
1655 :type integ_alg: Optional[IntegAlg]
1656 :type ikeys: Sequence[bytes]
1657 :type addr_incr: int
1659 :type existing_tunnels: int
1661 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1662 if not existing_tunnels:
1663 # Set IP address on VPP node 2 interface
1664 cmd = u"sw_interface_add_del_address"
1666 sw_if_index=InterfaceUtil.get_interface_index(
1667 nodes[u"DUT2"], if2_key
1671 prefix=IPUtil.create_prefix_object(
1672 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1676 err_msg = f"Failed to set IP address on interface {if2_key} " \
1677 f"on host {nodes[u'DUT2'][u'host']}"
1678 papi_exec.add(cmd, **args).get_reply(err_msg)
1679 # Configure IPIP tunnel interfaces
1680 cmd = u"ipip_add_tunnel"
1682 instance=Constants.BITWISE_NON_ZERO,
1687 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1689 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1690 dscp=int(IpDscp.IP_API_DSCP_CS0)
1695 ipip_tunnels = [None] * existing_tunnels
1696 for i in range(existing_tunnels, n_tunnels):
1697 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1700 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1701 tun_ips[u"ip1"] + i * addr_incr
1704 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1706 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1707 f" {nodes[u'DUT2'][u'host']}"
1708 ipip_tunnels.extend(
1710 reply[u"sw_if_index"]
1711 for reply in papi_exec.get_replies(err_msg)
1712 if u"sw_if_index" in reply
1715 # Configure IPSec SAD entries
1716 cmd = u"ipsec_sad_entry_add_del_v3"
1728 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1729 crypto_algorithm=crypto_alg.alg_int_repr,
1731 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1732 integrity_key=i_key,
1738 encap_decap_flags=int(
1739 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1741 dscp=int(IpDscp.IP_API_DSCP_CS0),
1744 udp_src_port=IPSEC_UDP_PORT_NONE,
1745 udp_dst_port=IPSEC_UDP_PORT_NONE,
1751 for i in range(existing_tunnels, n_tunnels):
1753 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1756 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1758 # SAD entry for outband / tx path
1759 args[u"entry"][u"sad_id"] = 100000 + i
1760 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1762 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1763 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1765 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1766 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1767 args[u"entry"][u"flags"] = int(
1768 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1771 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1773 # SAD entry for inband / rx path
1774 args[u"entry"][u"sad_id"] = i
1775 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1777 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1778 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1780 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1781 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1782 args[u"entry"][u"flags"] = int(
1783 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1784 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1787 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1789 err_msg = f"Failed to add IPsec SAD entries on host" \
1790 f" {nodes[u'DUT2'][u'host']}"
1791 papi_exec.get_replies(err_msg)
1792 # Add protection for tunnels with IPSEC
1793 cmd = u"ipsec_tunnel_protect_update"
1796 via_label=MPLS_LABEL_INVALID,
1797 obj_id=Constants.BITWISE_NON_ZERO
1799 ipsec_tunnel_protect = dict(
1807 tunnel=ipsec_tunnel_protect
1809 for i in range(existing_tunnels, n_tunnels):
1810 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1811 args[u"tunnel"][u"sa_out"] = 100000 + i
1812 args[u"tunnel"][u"sa_in"] = [i]
1814 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1816 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1817 f"on host {nodes[u'DUT2'][u'host']}"
1818 papi_exec.get_replies(err_msg)
1820 if not existing_tunnels:
1821 # Configure IP route
1822 cmd = u"ip_route_add_del"
1823 route = IPUtil.compose_vpp_route_structure(
1824 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1825 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1827 gateway=(tun_ips[u"ip2"] - 1).compressed
1834 papi_exec.add(cmd, **args)
1835 # Configure unnumbered interfaces
1836 cmd = u"sw_interface_set_unnumbered"
1839 sw_if_index=InterfaceUtil.get_interface_index(
1840 nodes[u"DUT2"], if2_key
1842 unnumbered_sw_if_index=0
1844 for i in range(existing_tunnels, n_tunnels):
1845 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1847 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1850 cmd = u"sw_interface_set_flags"
1853 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1855 for i in range(existing_tunnels, n_tunnels):
1856 args[u"sw_if_index"] = ipip_tunnels[i]
1858 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1860 # Configure IP routes
1861 cmd = u"ip_route_add_del"
1867 for i in range(existing_tunnels, n_tunnels):
1868 args[u"route"] = IPUtil.compose_vpp_route_structure(
1869 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1870 prefix_len=128 if raddr_ip1.version == 6 else 32,
1871 interface=ipip_tunnels[i]
1874 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1876 err_msg = f"Failed to add IP routes " \
1877 f"on host {nodes[u'DUT2'][u'host']}"
1878 papi_exec.get_replies(err_msg)
1881 def vpp_ipsec_create_tunnel_interfaces(
1882 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1883 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1884 existing_tunnels=0, return_keys=False):
1885 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1887 Some deployments (e.g. devicetest) need to know the generated keys.
1888 But other deployments (e.g. scale perf test) would get spammed
1889 if we returned keys every time.
1891 :param nodes: VPP nodes to create tunnel interfaces.
1892 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1894 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1896 :param if1_key: VPP node 1 interface key from topology file.
1897 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1898 interface key from topology file.
1899 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1900 :param crypto_alg: The encryption algorithm name.
1901 :param integ_alg: The integrity algorithm name.
1902 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1903 first tunnel in direction node1->node2.
1904 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1905 first tunnel in direction node2->node1.
1906 :param raddr_range: Mask specifying range of Policy selector Remote
1907 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1908 and to 128 in case of IPv6.
1909 :param existing_tunnels: Number of tunnel interfaces before creation.
1910 Useful mainly for reconf tests. Default 0.
1911 :param return_keys: Whether generated keys should be returned.
1913 :type tun_if1_ip_addr: str
1914 :type tun_if2_ip_addr: str
1917 :type n_tunnels: int
1918 :type crypto_alg: CryptoAlg
1919 :type integ_alg: Optonal[IntegAlg]
1920 :type raddr_ip1: string
1921 :type raddr_ip2: string
1922 :type raddr_range: int
1923 :type existing_tunnels: int
1924 :type return_keys: bool
1925 :returns: Ckeys, ikeys, spi_1, spi_2.
1926 :rtype: Optional[List[bytes], List[bytes], int, int]
1928 n_tunnels = int(n_tunnels)
1929 existing_tunnels = int(existing_tunnels)
1935 ip1=ip_address(tun_if1_ip_addr),
1936 ip2=ip_address(tun_if2_ip_addr)
1938 raddr_ip1 = ip_address(raddr_ip1)
1939 raddr_ip2 = ip_address(raddr_ip2)
1940 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1941 else 1 << (32 - raddr_range)
1943 if n_tunnels - existing_tunnels > 10:
1944 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1945 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1946 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1948 if u"DUT2" in nodes.keys():
1949 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1950 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1951 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1955 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1956 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1957 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1959 if u"DUT2" in nodes.keys():
1960 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1961 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1962 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1967 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1971 def _create_ipsec_script_files(dut, instances):
1972 """Create script files for configuring IPsec in containers
1974 :param dut: DUT node on which to create the script files
1975 :param instances: number of containers on DUT node
1977 :type instances: int
1980 for cnf in range(0, instances):
1982 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1984 scripts.append(open(script_filename, 'w'))
1988 def _close_and_copy_ipsec_script_files(
1989 dut, nodes, instances, scripts):
1990 """Close created scripts and copy them to containers
1992 :param dut: DUT node on which to create the script files
1993 :param nodes: VPP nodes
1994 :param instances: number of containers on DUT node
1995 :param scripts: dictionary holding the script files
1998 :type instances: int
2001 for cnf in range(0, instances):
2002 scripts[cnf].close()
2004 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
2006 scp_node(nodes[dut], script_filename, script_filename)
2010 def vpp_ipsec_create_tunnel_interfaces_in_containers(
2011 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
2012 raddr_ip1, raddr_ip2, raddr_range, n_instances):
2013 """Create multiple IPsec tunnel interfaces between two VPP nodes.
2015 :param nodes: VPP nodes to create tunnel interfaces.
2016 :param if1_ip_addr: VPP node 1 interface IP4 address.
2017 :param if2_ip_addr: VPP node 2 interface IP4 address.
2018 :param n_tunnels: Number of tunnell interfaces to create.
2019 :param crypto_alg: The encryption algorithm name.
2020 :param integ_alg: The integrity algorithm name.
2021 :param raddr_ip1: Policy selector remote IPv4 start address for the
2022 first tunnel in direction node1->node2.
2023 :param raddr_ip2: Policy selector remote IPv4 start address for the
2024 first tunnel in direction node2->node1.
2025 :param raddr_range: Mask specifying range of Policy selector Remote
2026 IPv4 addresses. Valid values are from 1 to 32.
2027 :param n_instances: Number of containers.
2029 :type if1_ip_addr: str
2030 :type if2_ip_addr: str
2031 :type n_tunnels: int
2032 :type crypto_alg: CryptoAlg
2033 :type integ_alg: Optional[IntegAlg]
2034 :type raddr_ip1: string
2035 :type raddr_ip2: string
2036 :type raddr_range: int
2037 :type n_instances: int
2041 addr_incr = 1 << (32 - raddr_range)
2043 dut1_scripts = IPsecUtil._create_ipsec_script_files(
2044 u"DUT1", n_instances
2046 dut2_scripts = IPsecUtil._create_ipsec_script_files(
2047 u"DUT2", n_instances
2050 for cnf in range(0, n_instances):
2051 dut1_scripts[cnf].write(
2052 u"create loopback interface\n"
2053 u"set interface state loop0 up\n\n"
2055 dut2_scripts[cnf].write(
2056 f"ip route add {if1_ip_addr}/8 via "
2057 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
2060 for tnl in range(0, n_tunnels):
2061 cnf = tnl % n_instances
2063 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
2067 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
2071 f"integ-alg {integ_alg.alg_name} "
2072 f"local-integ-key {ikey} "
2073 f"remote-integ-key {ikey} "
2075 # Configure tunnel end point(s) on left side
2076 dut1_scripts[cnf].write(
2077 u"set interface ip address loop0 "
2078 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
2079 f"create ipsec tunnel "
2080 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2081 f"local-spi {spi_1 + tnl} "
2082 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
2083 f"remote-spi {spi_2 + tnl} "
2084 f"crypto-alg {crypto_alg.alg_name} "
2085 f"local-crypto-key {ckey} "
2086 f"remote-crypto-key {ckey} "
2087 f"instance {tnl // n_instances} "
2090 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
2091 f"set interface state ipip{tnl // n_instances} up\n"
2092 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
2093 f"via ipip{tnl // n_instances}\n\n"
2095 # Configure tunnel end point(s) on right side
2096 dut2_scripts[cnf].write(
2097 f"set ip neighbor memif1/{cnf + 1} "
2098 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
2099 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
2100 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
2101 f"local-spi {spi_2 + tnl} "
2102 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2103 f"remote-spi {spi_1 + tnl} "
2104 f"crypto-alg {crypto_alg.alg_name} "
2105 f"local-crypto-key {ckey} "
2106 f"remote-crypto-key {ckey} "
2107 f"instance {tnl // n_instances} "
2110 f"set interface unnumbered ipip{tnl // n_instances} "
2111 f"use memif1/{cnf + 1}\n"
2112 f"set interface state ipip{tnl // n_instances} up\n"
2113 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
2114 f"via ipip{tnl // n_instances}\n\n"
2117 IPsecUtil._close_and_copy_ipsec_script_files(
2118 u"DUT1", nodes, n_instances, dut1_scripts)
2119 IPsecUtil._close_and_copy_ipsec_script_files(
2120 u"DUT2", nodes, n_instances, dut2_scripts)
2123 def vpp_ipsec_add_multiple_tunnels(
2124 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
2125 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range,
2126 tunnel_addr_incr=True):
2127 """Create multiple IPsec tunnels between two VPP nodes.
2129 :param nodes: VPP nodes to create tunnels.
2130 :param interface1: Interface name or sw_if_index on node 1.
2131 :param interface2: Interface name or sw_if_index on node 2.
2132 :param n_tunnels: Number of tunnels to create.
2133 :param crypto_alg: The encryption algorithm name.
2134 :param integ_alg: The integrity algorithm name.
2135 :param tunnel_ip1: Tunnel node1 IPv4 address.
2136 :param tunnel_ip2: Tunnel node2 IPv4 address.
2137 :param raddr_ip1: Policy selector remote IPv4 start address for the
2138 first tunnel in direction node1->node2.
2139 :param raddr_ip2: Policy selector remote IPv4 start address for the
2140 first tunnel in direction node2->node1.
2141 :param raddr_range: Mask specifying range of Policy selector Remote
2142 IPv4 addresses. Valid values are from 1 to 32.
2143 :param tunnel_addr_incr: Enable or disable tunnel IP address
2146 :type interface1: str or int
2147 :type interface2: str or int
2148 :type n_tunnels: int
2149 :type crypto_alg: CryptoAlg
2150 :type integ_alg: Optional[IntegAlg]
2151 :type tunnel_ip1: str
2152 :type tunnel_ip2: str
2153 :type raddr_ip1: string
2154 :type raddr_ip2: string
2155 :type raddr_range: int
2156 :type tunnel_addr_incr: bool
2165 dut1_local_outbound_range = ip_network(f"{tunnel_ip1}/8", False).\
2167 dut1_remote_outbound_range = ip_network(f"{tunnel_ip2}/8", False).\
2170 crypto_key = gen_key(
2171 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2173 integ_key = gen_key(
2174 IPsecUtil.get_integ_alg_key_len(integ_alg)
2175 ).decode() if integ_alg else u""
2177 rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
2178 if u"DUT2" in nodes.keys() \
2179 else Topology.get_interface_mac(nodes[u"TG"], interface2)
2180 IPsecUtil.vpp_ipsec_set_ip_route(
2181 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
2182 interface1, raddr_range, rmac)
2184 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
2185 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
2186 IPsecUtil.vpp_ipsec_add_spd_entry(
2187 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
2188 proto=50, laddr_range=dut1_local_outbound_range,
2189 raddr_range=dut1_remote_outbound_range
2191 IPsecUtil.vpp_ipsec_add_spd_entry(
2192 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
2193 proto=50, laddr_range=dut1_remote_outbound_range,
2194 raddr_range=dut1_local_outbound_range
2197 IPsecUtil.vpp_ipsec_add_sad_entries(
2198 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
2199 integ_alg, integ_key, tunnel_ip1, tunnel_ip2, tunnel_addr_incr
2202 IPsecUtil.vpp_ipsec_add_spd_entries(
2203 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2204 action=PolicyAction.PROTECT, inbound=False,
2205 sa_id=ObjIncrement(sa_id_1, 1),
2206 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2209 IPsecUtil.vpp_ipsec_add_sad_entries(
2210 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
2211 integ_alg, integ_key, tunnel_ip2, tunnel_ip1, tunnel_addr_incr
2213 IPsecUtil.vpp_ipsec_add_spd_entries(
2214 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2215 action=PolicyAction.PROTECT, inbound=True,
2216 sa_id=ObjIncrement(sa_id_2, 1),
2217 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2220 if u"DUT2" in nodes.keys():
2221 rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
2222 IPsecUtil.vpp_ipsec_set_ip_route(
2223 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2224 interface2, raddr_range, rmac)
2226 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2227 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2228 IPsecUtil.vpp_ipsec_add_spd_entry(
2229 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2230 inbound=False, proto=50, laddr_range=dut1_remote_outbound_range,
2231 raddr_range=dut1_local_outbound_range
2233 IPsecUtil.vpp_ipsec_add_spd_entry(
2234 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2235 inbound=True, proto=50, laddr_range=dut1_local_outbound_range,
2236 raddr_range=dut1_remote_outbound_range
2239 IPsecUtil.vpp_ipsec_add_sad_entries(
2240 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2241 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2,
2244 IPsecUtil.vpp_ipsec_add_spd_entries(
2245 nodes[u"DUT2"], n_tunnels, spd_id,
2246 priority=ObjIncrement(p_lo, 0),
2247 action=PolicyAction.PROTECT, inbound=True,
2248 sa_id=ObjIncrement(sa_id_1, 1),
2249 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2252 IPsecUtil.vpp_ipsec_add_sad_entries(
2253 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2254 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1,
2257 IPsecUtil.vpp_ipsec_add_spd_entries(
2258 nodes[u"DUT2"], n_tunnels, spd_id,
2259 priority=ObjIncrement(p_lo, 0),
2260 action=PolicyAction.PROTECT, inbound=False,
2261 sa_id=ObjIncrement(sa_id_2, 1),
2262 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2266 def vpp_ipsec_show_all(node):
2267 """Run "show ipsec all" debug CLI command.
2269 :param node: Node to run command on.
2272 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2275 def show_ipsec_security_association(node):
2276 """Show IPSec security association.
2278 :param node: DUT node.
2284 PapiSocketExecutor.dump_and_log(node, cmds)
2287 def vpp_ipsec_flow_enale_rss(node, proto, type, function="default"):
2288 """Ipsec flow enable rss action.
2290 :param node: DUT node.
2291 :param proto: The flow protocol.
2292 :param type: RSS type.
2293 :param function: RSS function.
2299 :returns: flow_index.
2301 # TODO: to be fixed to use full PAPI when it is ready in VPP
2302 cmd = f"test flow add src-ip any proto {proto} rss function " \
2303 f"{function} rss types {type}"
2304 stdout = PapiSocketExecutor.run_cli_cmd(node, cmd)
2305 flow_index = stdout.split()[1]
2310 def vpp_create_ipsec_flows_on_dut(
2311 node, n_flows, rx_queues, spi_start, interface):
2312 """Create mutiple ipsec flows and enable flows onto interface.
2314 :param node: DUT node.
2315 :param n_flows: Number of flows to create.
2316 :param rx_queues: NUmber of RX queues.
2317 :param spi_start: The start spi.
2318 :param interface: Name of the interface.
2322 :type rx_queues: int
2323 :type spi_start: int
2324 :type interface: str
2325 :returns: flow_index.
2328 for i in range(0, n_flows):
2329 rx_queue = i%rx_queues
2332 flow_index = FlowUtil.vpp_create_ip4_ipsec_flow(
2333 node, "ESP", spi, "redirect-to-queue", value=rx_queue)
2334 FlowUtil.vpp_flow_enable(node, interface, flow_index)