1 # Copyright (c) 2021 Cisco and/or its affiliates.
2 # Copyright (c) 2021 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
39 IPSEC_UDP_PORT_NONE = 0xffff
43 """Generate random string as a key.
45 :param length: Length of generated payload.
47 :returns: The generated payload.
51 choice(ascii_letters) for _ in range(length)
52 ).encode(encoding=u"utf-8")
55 class PolicyAction(Enum):
57 BYPASS = (u"bypass", 0)
58 DISCARD = (u"discard", 1)
59 PROTECT = (u"protect", 3)
61 def __init__(self, policy_name, policy_int_repr):
62 self.policy_name = policy_name
63 self.policy_int_repr = policy_int_repr
66 return self.policy_name
69 return self.policy_int_repr
72 class CryptoAlg(Enum):
73 """Encryption algorithms."""
74 AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
75 AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
76 AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
77 AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
79 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
80 self.alg_name = alg_name
81 self.alg_int_repr = alg_int_repr
82 self.scapy_name = scapy_name
83 self.key_len = key_len
87 """Integrity algorithm."""
88 SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
89 SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
91 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
92 self.alg_name = alg_name
93 self.alg_int_repr = alg_int_repr
94 self.scapy_name = scapy_name
95 self.key_len = key_len
98 class IPsecProto(IntEnum):
100 IPSEC_API_PROTO_ESP = 50
101 IPSEC_API_PROTO_AH = 51
104 class IPsecSadFlags(IntEnum):
105 """IPsec Security Association Database flags."""
106 IPSEC_API_SAD_FLAG_NONE = 0
107 # Enable extended sequence numbers
108 IPSEC_API_SAD_FLAG_USE_ESN = 0x01
109 # Enable Anti - replay
110 IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02
111 # IPsec tunnel mode if non-zero, else transport mode
112 IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04
113 # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
114 # only valid if is_tunnel is non-zero
115 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08
116 # Enable UDP encapsulation for NAT traversal
117 IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10
118 # IPsec SA is or inbound traffic
119 IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
122 class TunnelEncpaDecapFlags(IntEnum):
123 """Flags controlling tunnel behaviour."""
124 TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
125 # at encap, copy the DF bit of the payload into the tunnel header
126 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
127 # at encap, set the DF bit in the tunnel header
128 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
129 # at encap, copy the DSCP bits of the payload into the tunnel header
130 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
131 # at encap, copy the ECN bit of the payload into the tunnel header
132 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
133 # at decap, copy the ECN bit of the tunnel header into the payload
134 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
137 class TunnelMode(IntEnum):
140 TUNNEL_API_MODE_P2P = 0
142 TUNNEL_API_MODE_MP = 1
146 """IPsec utilities."""
149 def policy_action_bypass():
150 """Return policy action bypass.
152 :returns: PolicyAction enum BYPASS object.
155 return PolicyAction.BYPASS
158 def policy_action_discard():
159 """Return policy action discard.
161 :returns: PolicyAction enum DISCARD object.
164 return PolicyAction.DISCARD
167 def policy_action_protect():
168 """Return policy action protect.
170 :returns: PolicyAction enum PROTECT object.
173 return PolicyAction.PROTECT
176 def crypto_alg_aes_cbc_128():
177 """Return encryption algorithm aes-cbc-128.
179 :returns: CryptoAlg enum AES_CBC_128 object.
182 return CryptoAlg.AES_CBC_128
185 def crypto_alg_aes_cbc_256():
186 """Return encryption algorithm aes-cbc-256.
188 :returns: CryptoAlg enum AES_CBC_256 object.
191 return CryptoAlg.AES_CBC_256
194 def crypto_alg_aes_gcm_128():
195 """Return encryption algorithm aes-gcm-128.
197 :returns: CryptoAlg enum AES_GCM_128 object.
200 return CryptoAlg.AES_GCM_128
203 def crypto_alg_aes_gcm_256():
204 """Return encryption algorithm aes-gcm-256.
206 :returns: CryptoAlg enum AES_GCM_128 object.
209 return CryptoAlg.AES_GCM_256
212 def get_crypto_alg_key_len(crypto_alg):
213 """Return encryption algorithm key length.
215 :param crypto_alg: Encryption algorithm.
216 :type crypto_alg: CryptoAlg
217 :returns: Key length.
220 return crypto_alg.key_len
223 def get_crypto_alg_scapy_name(crypto_alg):
224 """Return encryption algorithm scapy name.
226 :param crypto_alg: Encryption algorithm.
227 :type crypto_alg: CryptoAlg
228 :returns: Algorithm scapy name.
231 return crypto_alg.scapy_name
234 def integ_alg_sha_256_128():
235 """Return integrity algorithm SHA-256-128.
237 :returns: IntegAlg enum SHA_256_128 object.
240 return IntegAlg.SHA_256_128
243 def integ_alg_sha_512_256():
244 """Return integrity algorithm SHA-512-256.
246 :returns: IntegAlg enum SHA_512_256 object.
249 return IntegAlg.SHA_512_256
252 def get_integ_alg_key_len(integ_alg):
253 """Return integrity algorithm key length.
255 None argument is accepted, returning zero.
257 :param integ_alg: Integrity algorithm.
258 :type integ_alg: Optional[IntegAlg]
259 :returns: Key length.
262 return 0 if integ_alg is None else integ_alg.key_len
265 def get_integ_alg_scapy_name(integ_alg):
266 """Return integrity algorithm scapy name.
268 :param integ_alg: Integrity algorithm.
269 :type integ_alg: IntegAlg
270 :returns: Algorithm scapy name.
273 return integ_alg.scapy_name
276 def ipsec_proto_esp():
277 """Return IPSec protocol ESP.
279 :returns: IPsecProto enum ESP object.
282 return int(IPsecProto.IPSEC_API_PROTO_ESP)
285 def ipsec_proto_ah():
286 """Return IPSec protocol AH.
288 :returns: IPsecProto enum AH object.
291 return int(IPsecProto.IPSEC_API_PROTO_AH)
294 def vpp_ipsec_select_backend(node, protocol, index=1):
295 """Select IPsec backend.
297 :param node: VPP node to select IPsec backend on.
298 :param protocol: IPsec protocol.
299 :param index: Backend index.
301 :type protocol: IPsecProto
303 :raises RuntimeError: If failed to select IPsec backend or if no API
306 cmd = u"ipsec_select_backend"
307 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
312 with PapiSocketExecutor(node) as papi_exec:
313 papi_exec.add(cmd, **args).get_reply(err_msg)
316 def vpp_ipsec_set_async_mode(node, async_enable=1):
317 """Set IPsec async mode on|off.
319 :param node: VPP node to set IPsec async mode.
320 :param async_enable: Async mode on or off.
322 :type async_enable: int
323 :raises RuntimeError: If failed to set IPsec async mode or if no API
326 cmd = u"ipsec_set_async_mode"
327 err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
329 async_enable=async_enable
331 with PapiSocketExecutor(node) as papi_exec:
332 papi_exec.add(cmd, **args).get_reply(err_msg)
335 def vpp_ipsec_crypto_sw_scheduler_set_worker(
336 node, workers, crypto_enable=False):
337 """Enable or disable crypto on specific vpp worker threads.
339 :param node: VPP node to enable or disable crypto for worker threads.
340 :param workers: List of VPP thread numbers.
341 :param crypto_enable: Disable or enable crypto work.
343 :type workers: Iterable[int]
344 :type crypto_enable: bool
345 :raises RuntimeError: If failed to enable or disable crypto for worker
346 thread or if no API reply received.
348 for worker in workers:
349 cmd = u"crypto_sw_scheduler_set_worker"
350 err_msg = f"Failed to disable/enable crypto for worker thread " \
351 f"on host {node[u'host']}"
353 worker_index=worker - 1,
354 crypto_enable=crypto_enable
356 with PapiSocketExecutor(node) as papi_exec:
357 papi_exec.add(cmd, **args).get_reply(err_msg)
360 def vpp_ipsec_crypto_sw_scheduler_set_worker_on_all_duts(
361 nodes, workers, crypto_enable=False):
362 """Enable or disable crypto on specific vpp worker threads.
364 :param node: VPP node to enable or disable crypto for worker threads.
365 :param workers: List of VPP thread numbers.
366 :param crypto_enable: Disable or enable crypto work.
368 :type workers: Iterable[int]
369 :type crypto_enable: bool
370 :raises RuntimeError: If failed to enable or disable crypto for worker
371 thread or if no API reply received.
373 for node in nodes.values():
374 if node[u"type"] == NodeType.DUT:
375 thread_data = VPPUtil.vpp_show_threads(node)
376 worker_cnt = len(thread_data) - 1
380 for item in thread_data:
381 if str(item.cpu_id) in workers.split(u","):
382 worker_ids.append(item.id)
384 IPsecUtil.vpp_ipsec_crypto_sw_scheduler_set_worker(
385 node, workers=worker_ids, crypto_enable=crypto_enable
389 def vpp_ipsec_add_sad_entry(
390 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
391 integ_key=u"", tunnel_src=None, tunnel_dst=None):
392 """Create Security Association Database entry on the VPP node.
394 :param node: VPP node to add SAD entry on.
395 :param sad_id: SAD entry ID.
396 :param spi: Security Parameter Index of this SAD entry.
397 :param crypto_alg: The encryption algorithm name.
398 :param crypto_key: The encryption key string.
399 :param integ_alg: The integrity algorithm name.
400 :param integ_key: The integrity key string.
401 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
402 specified ESP transport mode is used.
403 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
404 not specified ESP transport mode is used.
408 :type crypto_alg: CryptoAlg
409 :type crypto_key: str
410 :type integ_alg: Optional[IntegAlg]
412 :type tunnel_src: str
413 :type tunnel_dst: str
415 if isinstance(crypto_key, str):
416 crypto_key = crypto_key.encode(encoding=u"utf-8")
417 if isinstance(integ_key, str):
418 integ_key = integ_key.encode(encoding=u"utf-8")
420 length=len(crypto_key),
424 length=len(integ_key),
425 data=integ_key if integ_key else 0
428 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
429 if tunnel_src and tunnel_dst:
430 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
431 src_addr = ip_address(tunnel_src)
432 dst_addr = ip_address(tunnel_dst)
433 if src_addr.version == 6:
435 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
440 cmd = u"ipsec_sad_entry_add_del_v3"
441 err_msg = f"Failed to add Security Association Database entry " \
442 f"on host {node[u'host']}"
446 crypto_algorithm=crypto_alg.alg_int_repr,
448 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
455 encap_decap_flags=int(
456 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
458 dscp=int(IpDscp.IP_API_DSCP_CS0),
460 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
461 udp_src_port=4500, # default value in api
462 udp_dst_port=4500 # default value in api
468 with PapiSocketExecutor(node) as papi_exec:
469 papi_exec.add(cmd, **args).get_reply(err_msg)
472 def vpp_ipsec_add_sad_entries(
473 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
474 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
475 """Create multiple Security Association Database entries on VPP node.
477 :param node: VPP node to add SAD entry on.
478 :param n_entries: Number of SAD entries to be created.
479 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
481 :param spi: Security Parameter Index of first SAD entry. All subsequent
482 SAD entries will have spi incremented by 1.
483 :param crypto_alg: The encryption algorithm name.
484 :param crypto_key: The encryption key string.
485 :param integ_alg: The integrity algorithm name.
486 :param integ_key: The integrity key string.
487 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
488 specified ESP transport mode is used.
489 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
490 not specified ESP transport mode is used.
495 :type crypto_alg: CryptoAlg
496 :type crypto_key: str
497 :type integ_alg: Optional[IntegAlg]
499 :type tunnel_src: str
500 :type tunnel_dst: str
502 if isinstance(crypto_key, str):
503 crypto_key = crypto_key.encode(encoding=u"utf-8")
504 if isinstance(integ_key, str):
505 integ_key = integ_key.encode(encoding=u"utf-8")
506 if tunnel_src and tunnel_dst:
507 src_addr = ip_address(tunnel_src)
508 dst_addr = ip_address(tunnel_dst)
513 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
516 if int(n_entries) > 10:
517 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
519 with open(tmp_filename, 'w') as tmp_file:
520 for i in range(n_entries):
521 integ = f"integ-alg {integ_alg.alg_name} " \
522 f"integ-key {integ_key.hex()}" \
523 if integ_alg else u""
524 tunnel = f"tunnel src {src_addr + i * addr_incr} " \
525 f"tunnel dst {dst_addr + i * addr_incr}" \
526 if tunnel_src and tunnel_dst else u""
527 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
528 f"crypto-alg {crypto_alg.alg_name} " \
529 f"crypto-key {crypto_key.hex()} " \
530 f"{integ} {tunnel}\n"
534 tmp_filename, node, timeout=300, json_out=False,
537 os.remove(tmp_filename)
541 length=len(crypto_key),
545 length=len(integ_key),
546 data=integ_key if integ_key else 0
549 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
550 if tunnel_src and tunnel_dst:
551 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
552 if src_addr.version == 6:
554 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
557 cmd = u"ipsec_sad_entry_add_del_v3"
558 err_msg = f"Failed to add Security Association Database entry " \
559 f"on host {node[u'host']}"
564 crypto_algorithm=crypto_alg.alg_int_repr,
566 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
573 encap_decap_flags=int(
574 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
576 dscp=int(IpDscp.IP_API_DSCP_CS0),
578 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
579 udp_src_port=4500, # default value in api
580 udp_dst_port=4500 # default value in api
586 with PapiSocketExecutor(node) as papi_exec:
587 for i in range(n_entries):
588 args[u"entry"][u"sad_id"] = int(sad_id) + i
589 args[u"entry"][u"spi"] = int(spi) + i
590 args[u"entry"][u"tunnel"][u"src"] = (
591 str(src_addr + i * addr_incr)
592 if tunnel_src and tunnel_dst else src_addr
594 args[u"entry"][u"tunnel"][u"dst"] = (
595 str(dst_addr + i * addr_incr)
596 if tunnel_src and tunnel_dst else dst_addr
598 history = bool(not 1 < i < n_entries - 2)
599 papi_exec.add(cmd, history=history, **args)
600 papi_exec.get_replies(err_msg)
603 def vpp_ipsec_set_ip_route(
604 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
605 raddr_range, dst_mac=None):
606 """Set IP address and route on interface.
608 :param node: VPP node to add config on.
609 :param n_tunnels: Number of tunnels to create.
610 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
611 :param traffic_addr: Traffic destination IP address to route.
612 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
613 :param interface: Interface key on node 1.
614 :param raddr_range: Mask specifying range of Policy selector Remote IP
615 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
617 :param dst_mac: The MAC address of destination tunnels.
620 :type tunnel_src: str
621 :type traffic_addr: str
622 :type tunnel_dst: str
624 :type raddr_range: int
627 tunnel_src = ip_address(tunnel_src)
628 tunnel_dst = ip_address(tunnel_dst)
629 traffic_addr = ip_address(traffic_addr)
630 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
631 else 1 << (32 - raddr_range)
633 if int(n_tunnels) > 10:
634 tmp_filename = u"/tmp/ipsec_set_ip.script"
636 with open(tmp_filename, 'w') as tmp_file:
637 if_name = Topology.get_interface_name(node, interface)
638 for i in range(n_tunnels):
639 conf = f"exec set interface ip address {if_name} " \
640 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
641 f"exec ip route add {traffic_addr + i}/" \
642 f"{128 if traffic_addr.version == 6 else 32} " \
643 f"via {tunnel_dst + i * addr_incr} {if_name}\n"
645 conf = f"{conf}exec set ip neighbor {if_name} " \
646 f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
649 VatExecutor().execute_script(
650 tmp_filename, node, timeout=300, json_out=False,
653 os.remove(tmp_filename)
656 cmd1 = u"sw_interface_add_del_address"
658 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
663 cmd2 = u"ip_route_add_del"
669 cmd3 = u"ip_neighbor_add_del"
673 sw_if_index=Topology.get_interface_sw_index(node, interface),
675 mac_address=str(dst_mac),
679 err_msg = f"Failed to configure IP addresses, IP routes and " \
680 f"IP neighbor on interface {interface} on host {node[u'host']}" \
682 else f"Failed to configure IP addresses and IP routes " \
683 f"on interface {interface} on host {node[u'host']}"
685 with PapiSocketExecutor(node) as papi_exec:
686 for i in range(n_tunnels):
687 args1[u"prefix"] = IPUtil.create_prefix_object(
688 tunnel_src + i * addr_incr, raddr_range
690 args2[u"route"] = IPUtil.compose_vpp_route_structure(
691 node, traffic_addr + i,
692 prefix_len=128 if traffic_addr.version == 6 else 32,
693 interface=interface, gateway=tunnel_dst + i * addr_incr
695 history = bool(not 1 < i < n_tunnels - 2)
696 papi_exec.add(cmd1, history=history, **args1).\
697 add(cmd2, history=history, **args2)
699 args3[u"neighbor"][u"ip_address"] = ip_address(
700 tunnel_dst + i * addr_incr
702 papi_exec.add(cmd3, history=history, **args3)
703 papi_exec.get_replies(err_msg)
706 def vpp_ipsec_add_spd(node, spd_id):
707 """Create Security Policy Database on the VPP node.
709 :param node: VPP node to add SPD on.
710 :param spd_id: SPD ID.
714 cmd = u"ipsec_spd_add_del"
715 err_msg = f"Failed to add Security Policy Database " \
716 f"on host {node[u'host']}"
721 with PapiSocketExecutor(node) as papi_exec:
722 papi_exec.add(cmd, **args).get_reply(err_msg)
725 def vpp_ipsec_spd_add_if(node, spd_id, interface):
726 """Add interface to the Security Policy Database.
728 :param node: VPP node.
729 :param spd_id: SPD ID to add interface on.
730 :param interface: Interface name or sw_if_index.
733 :type interface: str or int
735 cmd = u"ipsec_interface_add_del_spd"
736 err_msg = f"Failed to add interface {interface} to Security Policy " \
737 f"Database {spd_id} on host {node[u'host']}"
740 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
743 with PapiSocketExecutor(node) as papi_exec:
744 papi_exec.add(cmd, **args).get_reply(err_msg)
747 def vpp_ipsec_create_spds_match_nth_entry(
748 node, dir1_interface, dir2_interface, entry_amount,
749 local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
750 inbound=False, bidirectional=True):
751 """Create one matching SPD entry for inbound or outbound traffic on
752 a DUT for each traffic direction and also create entry_amount - 1
753 non-matching SPD entries. Create a Security Policy Database on each
754 outbound interface where these entries will be configured.
755 The matching SPD entry will have the lowest priority, input action and
756 will be configured to match the IP flow. The non-matching entries will
757 be the same, except with higher priority and non-matching IP flows.
759 Action Protect is currently not supported.
761 :param node: VPP node to configured the SPDs and their entries.
762 :param dir1_interface: The interface in direction 1 where the entries
764 :param dir2_interface: The interface in direction 2 where the entries
766 :param entry_amount: The number of SPD entries to configure. If
767 entry_amount == 1, no non-matching entries will be configured.
768 :param local_addr_range: Matching local address range in direction 1
769 in format IP/prefix or IP/mask. If no mask is provided, it's
770 considered to be /32.
771 :param remote_addr_range: Matching remote address range in
772 direction 1 in format IP/prefix or IP/mask. If no mask is
773 provided, it's considered to be /32.
774 :param action: Policy action.
775 :param inbound: If True policy is for inbound traffic, otherwise
777 :param bidirectional: When True, will create SPDs in both directions
778 of traffic. When False, only in one direction.
780 :type dir1_interface: Union[string, int]
781 :type dir2_interface: Union[string, int]
782 :type entry_amount: int
783 :type local_addr_range:
784 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
785 :type remote_addr_range:
786 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
787 :type action: IPsecUtil.PolicyAction
789 :type bidirectional: bool
790 :raises NotImplementedError: When the action is PolicyAction.PROTECT.
793 if action == PolicyAction.PROTECT:
794 raise NotImplementedError('Policy action PROTECT is not supported.')
798 matching_priority = 1
800 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
801 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
802 # matching entry direction 1
803 IPsecUtil.vpp_ipsec_add_spd_entry(
804 node, spd_id_dir1, matching_priority, action,
805 inbound=inbound, laddr_range=local_addr_range,
806 raddr_range=remote_addr_range
810 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
811 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
813 # matching entry direction 2, the address ranges are switched
814 IPsecUtil.vpp_ipsec_add_spd_entry(
815 node, spd_id_dir2, matching_priority, action,
816 inbound=inbound, laddr_range=remote_addr_range,
817 raddr_range=local_addr_range
820 # non-matching entries
821 no_match_entry_amount = entry_amount - 1
822 if no_match_entry_amount > 0:
823 # create a NetworkIncrement representation of the network,
824 # then skip the matching network
825 no_match_local_addr_range = NetworkIncrement(
826 ip_network(local_addr_range)
828 next(no_match_local_addr_range)
830 no_match_remote_addr_range = NetworkIncrement(
831 ip_network(remote_addr_range)
833 next(no_match_remote_addr_range)
835 # non-matching entries direction 1
836 IPsecUtil.vpp_ipsec_add_spd_entries(
837 node, no_match_entry_amount, spd_id_dir1,
838 ObjIncrement(matching_priority + 1, 1), action,
839 inbound=inbound, laddr_range=no_match_local_addr_range,
840 raddr_range=no_match_remote_addr_range
844 # reset the networks so that we're using a unified config
845 # the address ranges are switched
846 no_match_remote_addr_range = NetworkIncrement(
847 ip_network(local_addr_range)
849 next(no_match_remote_addr_range)
851 no_match_local_addr_range = NetworkIncrement(
852 ip_network(remote_addr_range)
854 next(no_match_local_addr_range)
855 # non-matching entries direction 2
856 IPsecUtil.vpp_ipsec_add_spd_entries(
857 node, no_match_entry_amount, spd_id_dir2,
858 ObjIncrement(matching_priority + 1, 1), action,
859 inbound=inbound, laddr_range=no_match_local_addr_range,
860 raddr_range=no_match_remote_addr_range
863 IPsecUtil.vpp_ipsec_show_all(node)
866 def vpp_ipsec_add_spd_entry(
867 node, spd_id, priority, action, inbound=True, sa_id=None,
868 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
869 rport_range=None, is_ipv6=False):
870 """Create Security Policy Database entry on the VPP node.
872 :param node: VPP node to add SPD entry on.
873 :param spd_id: SPD ID to add entry on.
874 :param priority: SPD entry priority, higher number = higher priority.
875 :param action: Policy action.
876 :param inbound: If True policy is for inbound traffic, otherwise
878 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
879 :param proto: Policy selector next layer protocol number.
880 :param laddr_range: Policy selector local IPv4 or IPv6 address range
881 in format IP/prefix or IP/mask. If no mask is provided,
882 it's considered to be /32.
883 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
884 in format IP/prefix or IP/mask. If no mask is provided,
885 it's considered to be /32.
886 :param lport_range: Policy selector local TCP/UDP port range in format
887 <port_start>-<port_end>.
888 :param rport_range: Policy selector remote TCP/UDP port range in format
889 <port_start>-<port_end>.
890 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
891 not defined so it will default to address ::/0, otherwise False.
895 :type action: IPsecUtil.PolicyAction
899 :type laddr_range: string
900 :type raddr_range: string
901 :type lport_range: string
902 :type rport_range: string
905 if laddr_range is None:
906 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
908 if raddr_range is None:
909 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
911 local_net = ip_network(laddr_range, strict=False)
912 remote_net = ip_network(raddr_range, strict=False)
914 cmd = u"ipsec_spd_entry_add_del"
915 err_msg = f"Failed to add entry to Security Policy Database " \
916 f"{spd_id} on host {node[u'host']}"
920 priority=int(priority),
921 is_outbound=not inbound,
922 sa_id=int(sa_id) if sa_id else 0,
924 protocol=int(proto) if proto else 0,
925 remote_address_start=IPAddress.create_ip_address_object(
926 remote_net.network_address
928 remote_address_stop=IPAddress.create_ip_address_object(
929 remote_net.broadcast_address
931 local_address_start=IPAddress.create_ip_address_object(
932 local_net.network_address
934 local_address_stop=IPAddress.create_ip_address_object(
935 local_net.broadcast_address
937 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
939 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
941 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
943 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
950 with PapiSocketExecutor(node) as papi_exec:
951 papi_exec.add(cmd, **args).get_reply(err_msg)
954 def vpp_ipsec_add_spd_entries(
955 node, n_entries, spd_id, priority, action, inbound, sa_id=None,
956 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
957 rport_range=None, is_ipv6=False):
958 """Create multiple Security Policy Database entries on the VPP node.
960 :param node: VPP node to add SPD entries on.
961 :param n_entries: Number of SPD entries to be added.
962 :param spd_id: SPD ID to add entries on.
963 :param priority: SPD entries priority, higher number = higher priority.
964 :param action: Policy action.
965 :param inbound: If True policy is for inbound traffic, otherwise
967 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
968 :param proto: Policy selector next layer protocol number.
969 :param laddr_range: Policy selector local IPv4 or IPv6 address range
970 in format IP/prefix or IP/mask. If no mask is provided,
971 it's considered to be /32.
972 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
973 in format IP/prefix or IP/mask. If no mask is provided,
974 it's considered to be /32.
975 :param lport_range: Policy selector local TCP/UDP port range in format
976 <port_start>-<port_end>.
977 :param rport_range: Policy selector remote TCP/UDP port range in format
978 <port_start>-<port_end>.
979 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
980 not defined so it will default to address ::/0, otherwise False.
984 :type priority: IPsecUtil.ObjIncrement
985 :type action: IPsecUtil.PolicyAction
987 :type sa_id: IPsecUtil.ObjIncrement
989 :type laddr_range: IPsecUtil.NetworkIncrement
990 :type raddr_range: IPsecUtil.NetworkIncrement
991 :type lport_range: string
992 :type rport_range: string
995 if laddr_range is None:
996 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
997 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
999 if raddr_range is None:
1000 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1001 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1003 lport_range_start = 0
1004 lport_range_stop = 65535
1006 lport_range_start, lport_range_stop = lport_range.split('-')
1008 rport_range_start = 0
1009 rport_range_stop = 65535
1011 rport_range_start, rport_range_stop = rport_range.split('-')
1013 if int(n_entries) > 10:
1014 tmp_filename = f"/tmp/ipsec_spd_{spd_id}_add_del_entry.script"
1016 with open(tmp_filename, 'w') as tmp_file:
1017 for _ in range(n_entries):
1018 direction = u'inbound' if inbound else u'outbound'
1019 sa = f' sa {sa_id.inc_fmt()}' if sa_id is not None else ''
1020 protocol = f' protocol {protocol}' if proto else ''
1021 local_port_range = f' local-port-range ' \
1022 f'{lport_range_start} - {lport_range_stop}' \
1023 if lport_range else ''
1024 remote_port_range = f' remote-port-range ' \
1025 f'{rport_range_start} - {rport_range_stop}' \
1026 if rport_range else ''
1028 spd_cfg = f"exec ipsec policy add spd {spd_id} " \
1029 f"priority {priority.inc_fmt()} {direction}" \
1030 f"{protocol} action {action}{sa} " \
1031 f"local-ip-range {laddr_range.inc_fmt()} " \
1032 f"remote-ip-range {raddr_range.inc_fmt()}" \
1033 f"{local_port_range}{remote_port_range}\n"
1035 tmp_file.write(spd_cfg)
1037 VatExecutor().execute_script(
1038 tmp_filename, node, timeout=300, json_out=False,
1039 copy_on_execute=True
1041 os.remove(tmp_filename)
1044 for _ in range(n_entries):
1045 IPsecUtil.vpp_ipsec_add_spd_entry(
1046 node, spd_id, next(priority), action, inbound,
1047 next(sa_id) if sa_id is not None else sa_id,
1048 proto, next(laddr_range), next(raddr_range), lport_range,
1049 rport_range, is_ipv6
1053 def _ipsec_create_tunnel_interfaces_dut1_vat(
1054 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1055 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1056 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
1058 Generate random keys and return them (so DUT2 or TG can decrypt).
1060 :param nodes: VPP nodes to create tunnel interfaces.
1061 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1062 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1063 IPv4/IPv6 address (ip2).
1064 :param if1_key: VPP node 1 interface key from topology file.
1065 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1066 interface key from topology file.
1067 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1068 :param crypto_alg: The encryption algorithm name.
1069 :param integ_alg: The integrity algorithm name.
1070 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1071 first tunnel in direction node2->node1.
1072 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1073 :param addr_incr: IP / IPv6 address incremental step.
1074 :param existing_tunnels: Number of tunnel interfaces before creation.
1075 Useful mainly for reconf tests. Default 0.
1080 :type n_tunnels: int
1081 :type crypto_alg: CryptoAlg
1082 :type integ_alg: Optional[IntegAlg]
1083 :type raddr_ip2: IPv4Address or IPv6Address
1084 :type addr_incr: int
1086 :type existing_tunnels: int
1087 :returns: Generated ckeys and ikeys.
1088 :rtype: List[bytes], List[bytes]
1090 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
1091 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
1093 ckeys = [bytes()] * existing_tunnels
1094 ikeys = [bytes()] * existing_tunnels
1097 with open(tmp_fn1, u"w") as tmp_f1:
1098 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
1099 if u"DUT2" in nodes.keys() \
1100 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
1101 if not existing_tunnels:
1103 f"exec create loopback interface\n"
1104 f"exec set interface state loop0 up\n"
1105 f"exec set interface ip address {if1_n} "
1106 f"{tun_ips[u'ip2'] - 1}/"
1107 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
1108 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
1111 for i in range(existing_tunnels, n_tunnels):
1113 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1116 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1119 integ = f"integ-alg {integ_alg.alg_name} " \
1120 f"integ-key {ikeys[i].hex()} "
1124 f"exec set interface ip address loop0 "
1125 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
1126 f"exec create ipip tunnel "
1127 f"src {tun_ips[u'ip1'] + i * addr_incr} "
1128 f"dst {tun_ips[u'ip2']} "
1130 f"exec ipsec sa add {i} "
1131 f"spi {spi_d[u'spi_1'] + i} "
1132 f"crypto-alg {crypto_alg.alg_name} "
1133 f"crypto-key {ckeys[i].hex()} "
1136 f"exec ipsec sa add {100000 + i} "
1137 f"spi {spi_d[u'spi_2'] + i} "
1138 f"crypto-alg {crypto_alg.alg_name} "
1139 f"crypto-key {ckeys[i].hex()} "
1142 f"exec ipsec tunnel protect ipip{i} "
1144 f"sa-in {100000 + i} "
1148 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1149 copy_on_execute=True,
1150 history=bool(n_tunnels < 100)
1154 with open(tmp_fn1, 'w') as tmp_f1:
1155 for i in range(existing_tunnels, n_tunnels):
1157 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
1158 f"exec set interface state ipip{i} up\n"
1159 f"exec ip route add "
1160 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
1164 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1165 copy_on_execute=True,
1166 history=bool(n_tunnels < 100)
1173 def _ipsec_create_tunnel_interfaces_dut2_vat(
1174 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1175 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1176 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
1178 This method accesses keys generated by DUT1 method
1179 and does not return anything.
1181 :param nodes: VPP nodes to create tunnel interfaces.
1182 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1183 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1184 IPv4/IPv6 address (ip2).
1185 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1186 interface key from topology file.
1187 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1188 :param crypto_alg: The encryption algorithm name.
1189 :param ckeys: List of encryption keys.
1190 :param integ_alg: The integrity algorithm name.
1191 :param ikeys: List of integrity keys.
1192 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1193 :param addr_incr: IP / IPv6 address incremental step.
1194 :param existing_tunnels: Number of tunnel interfaces before creation.
1195 Useful mainly for reconf tests. Default 0.
1199 :type n_tunnels: int
1200 :type crypto_alg: CryptoAlg
1201 :type ckeys: Sequence[bytes]
1202 :type integ_alg: Optional[IntegAlg]
1203 :type ikeys: Sequence[bytes]
1204 :type addr_incr: int
1206 :type existing_tunnels: int
1208 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1209 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1212 with open(tmp_fn2, 'w') as tmp_f2:
1213 if not existing_tunnels:
1215 f"exec set interface ip address {if2_n}"
1216 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1218 for i in range(existing_tunnels, n_tunnels):
1220 integ = f"integ-alg {integ_alg.alg_name} " \
1221 f"integ-key {ikeys[i].hex()} "
1225 f"exec create ipip tunnel "
1226 f"src {tun_ips[u'ip2']} "
1227 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1229 f"exec ipsec sa add {100000 + i} "
1230 f"spi {spi_d[u'spi_2'] + i} "
1231 f"crypto-alg {crypto_alg.alg_name} "
1232 f"crypto-key {ckeys[i].hex()} "
1235 f"exec ipsec sa add {i} "
1236 f"spi {spi_d[u'spi_1'] + i} "
1237 f"crypto-alg {crypto_alg.alg_name} "
1238 f"crypto-key {ckeys[i].hex()} "
1241 f"exec ipsec tunnel protect ipip{i} "
1242 f"sa-out {100000 + i} "
1247 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1248 copy_on_execute=True,
1249 history=bool(n_tunnels < 100)
1253 with open(tmp_fn2, 'w') as tmp_f2:
1254 if not existing_tunnels:
1256 f"exec ip route add {tun_ips[u'ip1']}/8 "
1257 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1259 for i in range(existing_tunnels, n_tunnels):
1261 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1262 f"exec set interface state ipip{i} up\n"
1263 f"exec ip route add "
1264 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1268 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1269 copy_on_execute=True,
1270 history=bool(n_tunnels < 100)
1275 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1276 """Create loopback interface and set IP address on VPP node 1 interface
1279 :param nodes: VPP nodes to create tunnel interfaces.
1280 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1281 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1282 IPv4/IPv6 address (ip2).
1283 :param if1_key: VPP node 1 interface key from topology file.
1284 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1285 interface key from topology file.
1291 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1292 # Create loopback interface on DUT1, set it to up state
1293 cmd = u"create_loopback_instance"
1299 err_msg = f"Failed to create loopback interface " \
1300 f"on host {nodes[u'DUT1'][u'host']}"
1301 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1302 get_sw_if_index(err_msg)
1303 cmd = u"sw_interface_set_flags"
1305 sw_if_index=loop_sw_if_idx,
1306 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1308 err_msg = f"Failed to set loopback interface state up " \
1309 f"on host {nodes[u'DUT1'][u'host']}"
1310 papi_exec.add(cmd, **args).get_reply(err_msg)
1311 # Set IP address on VPP node 1 interface
1312 cmd = u"sw_interface_add_del_address"
1314 sw_if_index=InterfaceUtil.get_interface_index(
1315 nodes[u"DUT1"], if1_key
1319 prefix=IPUtil.create_prefix_object(
1320 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1324 err_msg = f"Failed to set IP address on interface {if1_key} " \
1325 f"on host {nodes[u'DUT1'][u'host']}"
1326 papi_exec.add(cmd, **args).get_reply(err_msg)
1327 cmd2 = u"ip_neighbor_add_del"
1331 sw_if_index=Topology.get_interface_sw_index(
1332 nodes[u"DUT1"], if1_key
1336 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1337 if u"DUT2" in nodes.keys()
1338 else Topology.get_interface_mac(
1339 nodes[u"TG"], if2_key
1342 ip_address=tun_ips[u"ip2"].compressed
1345 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1346 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1348 return loop_sw_if_idx
1351 def _ipsec_create_tunnel_interfaces_dut1_papi(
1352 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1353 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1354 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1356 Generate random keys and return them (so DUT2 or TG can decrypt).
1358 :param nodes: VPP nodes to create tunnel interfaces.
1359 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1360 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1361 IPv4/IPv6 address (ip2).
1362 :param if1_key: VPP node 1 interface key from topology file.
1363 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1364 interface key from topology file.
1365 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1366 :param crypto_alg: The encryption algorithm name.
1367 :param integ_alg: The integrity algorithm name.
1368 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1369 first tunnel in direction node2->node1.
1370 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1371 :param addr_incr: IP / IPv6 address incremental step.
1372 :param existing_tunnels: Number of tunnel interfaces before creation.
1373 Useful mainly for reconf tests. Default 0.
1378 :type n_tunnels: int
1379 :type crypto_alg: CryptoAlg
1380 :type integ_alg: Optional[IntegAlg]
1381 :type raddr_ip2: IPv4Address or IPv6Address
1382 :type addr_incr: int
1384 :type existing_tunnels: int
1385 :returns: Generated ckeys and ikeys.
1386 :rtype: List[bytes], List[bytes]
1388 if not existing_tunnels:
1389 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1390 nodes, tun_ips, if1_key, if2_key
1393 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1394 nodes[u"DUT1"], u"loop0"
1396 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1397 # Configure IP addresses on loop0 interface
1398 cmd = u"sw_interface_add_del_address"
1400 sw_if_index=loop_sw_if_idx,
1405 for i in range(existing_tunnels, n_tunnels):
1406 args[u"prefix"] = IPUtil.create_prefix_object(
1407 tun_ips[u"ip1"] + i * addr_incr,
1408 128 if tun_ips[u"ip1"].version == 6 else 32
1411 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1413 # Configure IPIP tunnel interfaces
1414 cmd = u"ipip_add_tunnel"
1416 instance=Constants.BITWISE_NON_ZERO,
1421 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1423 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1424 dscp=int(IpDscp.IP_API_DSCP_CS0)
1429 ipip_tunnels = [None] * existing_tunnels
1430 for i in range(existing_tunnels, n_tunnels):
1431 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1432 tun_ips[u"ip1"] + i * addr_incr
1434 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1438 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1440 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1441 f" {nodes[u'DUT1'][u'host']}"
1442 ipip_tunnels.extend(
1444 reply[u"sw_if_index"]
1445 for reply in papi_exec.get_replies(err_msg)
1446 if u"sw_if_index" in reply
1449 # Configure IPSec SAD entries
1450 ckeys = [bytes()] * existing_tunnels
1451 ikeys = [bytes()] * existing_tunnels
1452 cmd = u"ipsec_sad_entry_add_del_v3"
1464 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1465 crypto_algorithm=crypto_alg.alg_int_repr,
1467 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1468 integrity_key=i_key,
1474 encap_decap_flags=int(
1475 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1477 dscp=int(IpDscp.IP_API_DSCP_CS0),
1480 udp_src_port=IPSEC_UDP_PORT_NONE,
1481 udp_dst_port=IPSEC_UDP_PORT_NONE,
1487 for i in range(existing_tunnels, n_tunnels):
1489 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1492 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1494 # SAD entry for outband / tx path
1495 args[u"entry"][u"sad_id"] = i
1496 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1498 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1499 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1501 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1502 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1503 args[u"entry"][u"flags"] = int(
1504 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1507 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1509 # SAD entry for inband / rx path
1510 args[u"entry"][u"sad_id"] = 100000 + i
1511 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1513 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1514 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1516 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1517 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1518 args[u"entry"][u"flags"] = int(
1519 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1520 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1523 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1525 err_msg = f"Failed to add IPsec SAD entries on host" \
1526 f" {nodes[u'DUT1'][u'host']}"
1527 papi_exec.get_replies(err_msg)
1528 # Add protection for tunnels with IPSEC
1529 cmd = u"ipsec_tunnel_protect_update"
1532 via_label=MPLS_LABEL_INVALID,
1533 obj_id=Constants.BITWISE_NON_ZERO
1535 ipsec_tunnel_protect = dict(
1543 tunnel=ipsec_tunnel_protect
1545 for i in range(existing_tunnels, n_tunnels):
1546 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1547 args[u"tunnel"][u"sa_out"] = i
1548 args[u"tunnel"][u"sa_in"] = [100000 + i]
1550 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1552 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1553 f"on host {nodes[u'DUT1'][u'host']}"
1554 papi_exec.get_replies(err_msg)
1556 # Configure unnumbered interfaces
1557 cmd = u"sw_interface_set_unnumbered"
1560 sw_if_index=InterfaceUtil.get_interface_index(
1561 nodes[u"DUT1"], if1_key
1563 unnumbered_sw_if_index=0
1565 for i in range(existing_tunnels, n_tunnels):
1566 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1568 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1571 cmd = u"sw_interface_set_flags"
1574 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1576 for i in range(existing_tunnels, n_tunnels):
1577 args[u"sw_if_index"] = ipip_tunnels[i]
1579 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1581 # Configure IP routes
1582 cmd = u"ip_route_add_del"
1588 for i in range(existing_tunnels, n_tunnels):
1589 args[u"route"] = IPUtil.compose_vpp_route_structure(
1590 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1591 prefix_len=128 if raddr_ip2.version == 6 else 32,
1592 interface=ipip_tunnels[i]
1595 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1597 err_msg = f"Failed to add IP routes on host " \
1598 f"{nodes[u'DUT1'][u'host']}"
1599 papi_exec.get_replies(err_msg)
1604 def _ipsec_create_tunnel_interfaces_dut2_papi(
1605 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1606 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1607 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1609 This method accesses keys generated by DUT1 method
1610 and does not return anything.
1612 :param nodes: VPP nodes to create tunnel interfaces.
1613 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1614 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1615 IPv4/IPv6 address (ip2).
1616 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1617 interface key from topology file.
1618 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1619 :param crypto_alg: The encryption algorithm name.
1620 :param ckeys: List of encryption keys.
1621 :param integ_alg: The integrity algorithm name.
1622 :param ikeys: List of integrity keys.
1623 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1624 :param addr_incr: IP / IPv6 address incremental step.
1625 :param existing_tunnels: Number of tunnel interfaces before creation.
1626 Useful mainly for reconf tests. Default 0.
1630 :type n_tunnels: int
1631 :type crypto_alg: CryptoAlg
1632 :type ckeys: Sequence[bytes]
1633 :type integ_alg: Optional[IntegAlg]
1634 :type ikeys: Sequence[bytes]
1635 :type addr_incr: int
1637 :type existing_tunnels: int
1639 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1640 if not existing_tunnels:
1641 # Set IP address on VPP node 2 interface
1642 cmd = u"sw_interface_add_del_address"
1644 sw_if_index=InterfaceUtil.get_interface_index(
1645 nodes[u"DUT2"], if2_key
1649 prefix=IPUtil.create_prefix_object(
1650 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1654 err_msg = f"Failed to set IP address on interface {if2_key} " \
1655 f"on host {nodes[u'DUT2'][u'host']}"
1656 papi_exec.add(cmd, **args).get_reply(err_msg)
1657 # Configure IPIP tunnel interfaces
1658 cmd = u"ipip_add_tunnel"
1660 instance=Constants.BITWISE_NON_ZERO,
1665 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1667 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1668 dscp=int(IpDscp.IP_API_DSCP_CS0)
1673 ipip_tunnels = [None] * existing_tunnels
1674 for i in range(existing_tunnels, n_tunnels):
1675 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1678 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1679 tun_ips[u"ip1"] + i * addr_incr
1682 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1684 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1685 f" {nodes[u'DUT2'][u'host']}"
1686 ipip_tunnels.extend(
1688 reply[u"sw_if_index"]
1689 for reply in papi_exec.get_replies(err_msg)
1690 if u"sw_if_index" in reply
1693 # Configure IPSec SAD entries
1694 cmd = u"ipsec_sad_entry_add_del_v3"
1706 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1707 crypto_algorithm=crypto_alg.alg_int_repr,
1709 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1710 integrity_key=i_key,
1716 encap_decap_flags=int(
1717 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1719 dscp=int(IpDscp.IP_API_DSCP_CS0),
1722 udp_src_port=IPSEC_UDP_PORT_NONE,
1723 udp_dst_port=IPSEC_UDP_PORT_NONE,
1729 for i in range(existing_tunnels, n_tunnels):
1731 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1734 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1736 # SAD entry for outband / tx path
1737 args[u"entry"][u"sad_id"] = 100000 + i
1738 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1740 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1741 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1743 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1744 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1745 args[u"entry"][u"flags"] = int(
1746 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1749 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1751 # SAD entry for inband / rx path
1752 args[u"entry"][u"sad_id"] = i
1753 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1755 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1756 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1758 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1759 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1760 args[u"entry"][u"flags"] = int(
1761 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1762 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1765 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1767 err_msg = f"Failed to add IPsec SAD entries on host" \
1768 f" {nodes[u'DUT2'][u'host']}"
1769 papi_exec.get_replies(err_msg)
1770 # Add protection for tunnels with IPSEC
1771 cmd = u"ipsec_tunnel_protect_update"
1774 via_label=MPLS_LABEL_INVALID,
1775 obj_id=Constants.BITWISE_NON_ZERO
1777 ipsec_tunnel_protect = dict(
1785 tunnel=ipsec_tunnel_protect
1787 for i in range(existing_tunnels, n_tunnels):
1788 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1789 args[u"tunnel"][u"sa_out"] = 100000 + i
1790 args[u"tunnel"][u"sa_in"] = [i]
1792 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1794 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1795 f"on host {nodes[u'DUT2'][u'host']}"
1796 papi_exec.get_replies(err_msg)
1798 if not existing_tunnels:
1799 # Configure IP route
1800 cmd = u"ip_route_add_del"
1801 route = IPUtil.compose_vpp_route_structure(
1802 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1803 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1805 gateway=(tun_ips[u"ip2"] - 1).compressed
1812 papi_exec.add(cmd, **args)
1813 # Configure unnumbered interfaces
1814 cmd = u"sw_interface_set_unnumbered"
1817 sw_if_index=InterfaceUtil.get_interface_index(
1818 nodes[u"DUT2"], if2_key
1820 unnumbered_sw_if_index=0
1822 for i in range(existing_tunnels, n_tunnels):
1823 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1825 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1828 cmd = u"sw_interface_set_flags"
1831 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1833 for i in range(existing_tunnels, n_tunnels):
1834 args[u"sw_if_index"] = ipip_tunnels[i]
1836 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1838 # Configure IP routes
1839 cmd = u"ip_route_add_del"
1845 for i in range(existing_tunnels, n_tunnels):
1846 args[u"route"] = IPUtil.compose_vpp_route_structure(
1847 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1848 prefix_len=128 if raddr_ip1.version == 6 else 32,
1849 interface=ipip_tunnels[i]
1852 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1854 err_msg = f"Failed to add IP routes " \
1855 f"on host {nodes[u'DUT2'][u'host']}"
1856 papi_exec.get_replies(err_msg)
1859 def vpp_ipsec_create_tunnel_interfaces(
1860 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1861 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1862 existing_tunnels=0, return_keys=False):
1863 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1865 Some deployments (e.g. devicetest) need to know the generated keys.
1866 But other deployments (e.g. scale perf test) would get spammed
1867 if we returned keys every time.
1869 :param nodes: VPP nodes to create tunnel interfaces.
1870 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1872 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1874 :param if1_key: VPP node 1 interface key from topology file.
1875 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1876 interface key from topology file.
1877 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1878 :param crypto_alg: The encryption algorithm name.
1879 :param integ_alg: The integrity algorithm name.
1880 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1881 first tunnel in direction node1->node2.
1882 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1883 first tunnel in direction node2->node1.
1884 :param raddr_range: Mask specifying range of Policy selector Remote
1885 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1886 and to 128 in case of IPv6.
1887 :param existing_tunnels: Number of tunnel interfaces before creation.
1888 Useful mainly for reconf tests. Default 0.
1889 :param return_keys: Whether generated keys should be returned.
1891 :type tun_if1_ip_addr: str
1892 :type tun_if2_ip_addr: str
1895 :type n_tunnels: int
1896 :type crypto_alg: CryptoAlg
1897 :type integ_alg: Optonal[IntegAlg]
1898 :type raddr_ip1: string
1899 :type raddr_ip2: string
1900 :type raddr_range: int
1901 :type existing_tunnels: int
1902 :type return_keys: bool
1903 :returns: Ckeys, ikeys, spi_1, spi_2.
1904 :rtype: Optional[List[bytes], List[bytes], int, int]
1906 n_tunnels = int(n_tunnels)
1907 existing_tunnels = int(existing_tunnels)
1913 ip1=ip_address(tun_if1_ip_addr),
1914 ip2=ip_address(tun_if2_ip_addr)
1916 raddr_ip1 = ip_address(raddr_ip1)
1917 raddr_ip2 = ip_address(raddr_ip2)
1918 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1919 else 1 << (32 - raddr_range)
1921 if n_tunnels - existing_tunnels > 10:
1922 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1923 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1924 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1926 if u"DUT2" in nodes.keys():
1927 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1928 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1929 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1933 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1934 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1935 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1937 if u"DUT2" in nodes.keys():
1938 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1939 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1940 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1945 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1949 def _create_ipsec_script_files(dut, instances):
1950 """Create script files for configuring IPsec in containers
1952 :param dut: DUT node on which to create the script files
1953 :param instances: number of containers on DUT node
1955 :type instances: int
1958 for cnf in range(0, instances):
1960 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1962 scripts.append(open(script_filename, 'w'))
1966 def _close_and_copy_ipsec_script_files(
1967 dut, nodes, instances, scripts):
1968 """Close created scripts and copy them to containers
1970 :param dut: DUT node on which to create the script files
1971 :param nodes: VPP nodes
1972 :param instances: number of containers on DUT node
1973 :param scripts: dictionary holding the script files
1976 :type instances: int
1979 for cnf in range(0, instances):
1980 scripts[cnf].close()
1982 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1984 scp_node(nodes[dut], script_filename, script_filename)
1988 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1989 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1990 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1991 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1993 :param nodes: VPP nodes to create tunnel interfaces.
1994 :param if1_ip_addr: VPP node 1 interface IP4 address.
1995 :param if2_ip_addr: VPP node 2 interface IP4 address.
1996 :param n_tunnels: Number of tunnell interfaces to create.
1997 :param crypto_alg: The encryption algorithm name.
1998 :param integ_alg: The integrity algorithm name.
1999 :param raddr_ip1: Policy selector remote IPv4 start address for the
2000 first tunnel in direction node1->node2.
2001 :param raddr_ip2: Policy selector remote IPv4 start address for the
2002 first tunnel in direction node2->node1.
2003 :param raddr_range: Mask specifying range of Policy selector Remote
2004 IPv4 addresses. Valid values are from 1 to 32.
2005 :param n_instances: Number of containers.
2007 :type if1_ip_addr: str
2008 :type if2_ip_addr: str
2009 :type n_tunnels: int
2010 :type crypto_alg: CryptoAlg
2011 :type integ_alg: Optional[IntegAlg]
2012 :type raddr_ip1: string
2013 :type raddr_ip2: string
2014 :type raddr_range: int
2015 :type n_instances: int
2019 addr_incr = 1 << (32 - raddr_range)
2021 dut1_scripts = IPsecUtil._create_ipsec_script_files(
2022 u"DUT1", n_instances
2024 dut2_scripts = IPsecUtil._create_ipsec_script_files(
2025 u"DUT2", n_instances
2028 for cnf in range(0, n_instances):
2029 dut1_scripts[cnf].write(
2030 u"create loopback interface\n"
2031 u"set interface state loop0 up\n\n"
2033 dut2_scripts[cnf].write(
2034 f"ip route add {if1_ip_addr}/8 via "
2035 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
2038 for tnl in range(0, n_tunnels):
2039 cnf = tnl % n_instances
2041 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
2045 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
2049 f"integ-alg {integ_alg.alg_name} "
2050 f"local-integ-key {ikey} "
2051 f"remote-integ-key {ikey} "
2053 # Configure tunnel end point(s) on left side
2054 dut1_scripts[cnf].write(
2055 u"set interface ip address loop0 "
2056 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
2057 f"create ipsec tunnel "
2058 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2059 f"local-spi {spi_1 + tnl} "
2060 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
2061 f"remote-spi {spi_2 + tnl} "
2062 f"crypto-alg {crypto_alg.alg_name} "
2063 f"local-crypto-key {ckey} "
2064 f"remote-crypto-key {ckey} "
2065 f"instance {tnl // n_instances} "
2068 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
2069 f"set interface state ipip{tnl // n_instances} up\n"
2070 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
2071 f"via ipip{tnl // n_instances}\n\n"
2073 # Configure tunnel end point(s) on right side
2074 dut2_scripts[cnf].write(
2075 f"set ip neighbor memif1/{cnf + 1} "
2076 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
2077 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
2078 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
2079 f"local-spi {spi_2 + tnl} "
2080 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2081 f"remote-spi {spi_1 + tnl} "
2082 f"crypto-alg {crypto_alg.alg_name} "
2083 f"local-crypto-key {ckey} "
2084 f"remote-crypto-key {ckey} "
2085 f"instance {tnl // n_instances} "
2088 f"set interface unnumbered ipip{tnl // n_instances} "
2089 f"use memif1/{cnf + 1}\n"
2090 f"set interface state ipip{tnl // n_instances} up\n"
2091 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
2092 f"via ipip{tnl // n_instances}\n\n"
2095 IPsecUtil._close_and_copy_ipsec_script_files(
2096 u"DUT1", nodes, n_instances, dut1_scripts)
2097 IPsecUtil._close_and_copy_ipsec_script_files(
2098 u"DUT2", nodes, n_instances, dut2_scripts)
2101 def vpp_ipsec_add_multiple_tunnels(
2102 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
2103 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
2104 """Create multiple IPsec tunnels between two VPP nodes.
2106 :param nodes: VPP nodes to create tunnels.
2107 :param interface1: Interface name or sw_if_index on node 1.
2108 :param interface2: Interface name or sw_if_index on node 2.
2109 :param n_tunnels: Number of tunnels to create.
2110 :param crypto_alg: The encryption algorithm name.
2111 :param integ_alg: The integrity algorithm name.
2112 :param tunnel_ip1: Tunnel node1 IPv4 address.
2113 :param tunnel_ip2: Tunnel node2 IPv4 address.
2114 :param raddr_ip1: Policy selector remote IPv4 start address for the
2115 first tunnel in direction node1->node2.
2116 :param raddr_ip2: Policy selector remote IPv4 start address for the
2117 first tunnel in direction node2->node1.
2118 :param raddr_range: Mask specifying range of Policy selector Remote
2119 IPv4 addresses. Valid values are from 1 to 32.
2121 :type interface1: str or int
2122 :type interface2: str or int
2123 :type n_tunnels: int
2124 :type crypto_alg: CryptoAlg
2125 :type integ_alg: Optional[IntegAlg]
2126 :type tunnel_ip1: str
2127 :type tunnel_ip2: str
2128 :type raddr_ip1: string
2129 :type raddr_ip2: string
2130 :type raddr_range: int
2140 crypto_key = gen_key(
2141 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2143 integ_key = gen_key(
2144 IPsecUtil.get_integ_alg_key_len(integ_alg)
2145 ).decode() if integ_alg else u""
2147 rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
2148 if u"DUT2" in nodes.keys() \
2149 else Topology.get_interface_mac(nodes[u"TG"], interface2)
2150 IPsecUtil.vpp_ipsec_set_ip_route(
2151 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
2152 interface1, raddr_range, rmac)
2154 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
2155 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
2156 IPsecUtil.vpp_ipsec_add_spd_entry(
2157 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
2158 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
2160 IPsecUtil.vpp_ipsec_add_spd_entry(
2161 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
2162 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
2165 IPsecUtil.vpp_ipsec_add_sad_entries(
2166 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
2167 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
2170 IPsecUtil.vpp_ipsec_add_spd_entries(
2171 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2172 action=PolicyAction.PROTECT, inbound=False,
2173 sa_id=ObjIncrement(sa_id_1, 1),
2174 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2177 IPsecUtil.vpp_ipsec_add_sad_entries(
2178 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
2179 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2181 IPsecUtil.vpp_ipsec_add_spd_entries(
2182 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2183 action=PolicyAction.PROTECT, inbound=True,
2184 sa_id=ObjIncrement(sa_id_2, 1),
2185 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2188 if u"DUT2" in nodes.keys():
2189 IPsecUtil.vpp_ipsec_set_ip_route(
2190 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2191 interface2, raddr_range)
2193 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2194 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2195 IPsecUtil.vpp_ipsec_add_spd_entry(
2196 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2197 inbound=False, proto=50, laddr_range=u"100.0.0.0/8",
2198 raddr_range=u"100.0.0.0/8"
2200 IPsecUtil.vpp_ipsec_add_spd_entry(
2201 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2202 inbound=True, proto=50, laddr_range=u"100.0.0.0/8",
2203 raddr_range=u"100.0.0.0/8"
2206 IPsecUtil.vpp_ipsec_add_sad_entries(
2207 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2208 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2
2210 IPsecUtil.vpp_ipsec_add_spd_entries(
2211 nodes[u"DUT2"], n_tunnels, spd_id,
2212 priority=ObjIncrement(p_lo, 0),
2213 action=PolicyAction.PROTECT, inbound=True,
2214 sa_id=ObjIncrement(sa_id_1, 1),
2215 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2218 IPsecUtil.vpp_ipsec_add_sad_entries(
2219 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2220 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2222 IPsecUtil.vpp_ipsec_add_spd_entries(
2223 nodes[u"DUT2"], n_tunnels, spd_id,
2224 priority=ObjIncrement(p_lo, 0),
2225 action=PolicyAction.PROTECT, inbound=False,
2226 sa_id=ObjIncrement(sa_id_2, 1),
2227 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2231 def vpp_ipsec_show_all(node):
2232 """Run "show ipsec all" debug CLI command.
2234 :param node: Node to run command on.
2237 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2240 def show_ipsec_security_association(node):
2241 """Show IPSec security association.
2243 :param node: DUT node.
2249 PapiSocketExecutor.dump_and_log(node, cmds)