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
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 tunnel_dst_prefix = 128 if tunnel_dst.version == 6 else 32
631 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
632 else 1 << (32 - raddr_range)
634 if int(n_tunnels) > 10:
635 tmp_filename = u"/tmp/ipsec_set_ip.script"
637 with open(tmp_filename, 'w') as tmp_file:
638 if_name = Topology.get_interface_name(node, interface)
639 for i in range(n_tunnels):
640 tunnel_dst_addr = tunnel_dst + i * addr_incr
641 conf = f"exec set interface ip address {if_name} " \
642 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
643 f"exec ip route add {traffic_addr + i}/" \
644 f"{tunnel_dst_prefix} " \
645 f"via {tunnel_dst_addr} {if_name}\n" \
646 f"exec ip route add {tunnel_dst_addr}/" \
647 f"{tunnel_dst_prefix} " \
648 f"via {tunnel_dst_addr} {if_name}\n"
650 conf = f"{conf}exec set ip neighbor {if_name} " \
651 f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
654 VatExecutor().execute_script(
655 tmp_filename, node, timeout=300, json_out=False,
658 os.remove(tmp_filename)
661 cmd1 = u"sw_interface_add_del_address"
663 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
668 cmd2 = u"ip_route_add_del"
674 cmd3 = u"ip_neighbor_add_del"
678 sw_if_index=Topology.get_interface_sw_index(node, interface),
680 mac_address=str(dst_mac),
684 err_msg = f"Failed to configure IP addresses, IP routes and " \
685 f"IP neighbor on interface {interface} on host {node[u'host']}" \
687 else f"Failed to configure IP addresses and IP routes " \
688 f"on interface {interface} on host {node[u'host']}"
690 with PapiSocketExecutor(node) as papi_exec:
691 for i in range(n_tunnels):
692 tunnel_dst_addr = tunnel_dst + i * addr_incr
693 args1[u"prefix"] = IPUtil.create_prefix_object(
694 tunnel_src + i * addr_incr, raddr_range
696 args2[u"route"] = IPUtil.compose_vpp_route_structure(
697 node, traffic_addr + i,
698 prefix_len=tunnel_dst_prefix,
699 interface=interface, gateway=tunnel_dst_addr
701 history = bool(not 1 < i < n_tunnels - 2)
702 papi_exec.add(cmd1, history=history, **args1).\
703 add(cmd2, history=history, **args2)
705 args2[u"route"] = IPUtil.compose_vpp_route_structure(
706 node, tunnel_dst_addr,
707 prefix_len=tunnel_dst_prefix,
708 interface=interface, gateway=tunnel_dst_addr
710 papi_exec.add(cmd2, history=history, **args2)
713 args3[u"neighbor"][u"ip_address"] = ip_address(
716 papi_exec.add(cmd3, history=history, **args3)
717 papi_exec.get_replies(err_msg)
720 def vpp_ipsec_add_spd(node, spd_id):
721 """Create Security Policy Database on the VPP node.
723 :param node: VPP node to add SPD on.
724 :param spd_id: SPD ID.
728 cmd = u"ipsec_spd_add_del"
729 err_msg = f"Failed to add Security Policy Database " \
730 f"on host {node[u'host']}"
735 with PapiSocketExecutor(node) as papi_exec:
736 papi_exec.add(cmd, **args).get_reply(err_msg)
739 def vpp_ipsec_spd_add_if(node, spd_id, interface):
740 """Add interface to the Security Policy Database.
742 :param node: VPP node.
743 :param spd_id: SPD ID to add interface on.
744 :param interface: Interface name or sw_if_index.
747 :type interface: str or int
749 cmd = u"ipsec_interface_add_del_spd"
750 err_msg = f"Failed to add interface {interface} to Security Policy " \
751 f"Database {spd_id} on host {node[u'host']}"
754 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
757 with PapiSocketExecutor(node) as papi_exec:
758 papi_exec.add(cmd, **args).get_reply(err_msg)
761 def vpp_ipsec_create_spds_match_nth_entry(
762 node, dir1_interface, dir2_interface, entry_amount,
763 local_addr_range, remote_addr_range, action=PolicyAction.BYPASS,
764 inbound=False, bidirectional=True):
765 """Create one matching SPD entry for inbound or outbound traffic on
766 a DUT for each traffic direction and also create entry_amount - 1
767 non-matching SPD entries. Create a Security Policy Database on each
768 outbound interface where these entries will be configured.
769 The matching SPD entry will have the lowest priority, input action and
770 will be configured to match the IP flow. The non-matching entries will
771 be the same, except with higher priority and non-matching IP flows.
773 Action Protect is currently not supported.
775 :param node: VPP node to configured the SPDs and their entries.
776 :param dir1_interface: The interface in direction 1 where the entries
778 :param dir2_interface: The interface in direction 2 where the entries
780 :param entry_amount: The number of SPD entries to configure. If
781 entry_amount == 1, no non-matching entries will be configured.
782 :param local_addr_range: Matching local address range in direction 1
783 in format IP/prefix or IP/mask. If no mask is provided, it's
784 considered to be /32.
785 :param remote_addr_range: Matching remote address range in
786 direction 1 in format IP/prefix or IP/mask. If no mask is
787 provided, it's considered to be /32.
788 :param action: Policy action.
789 :param inbound: If True policy is for inbound traffic, otherwise
791 :param bidirectional: When True, will create SPDs in both directions
792 of traffic. When False, only in one direction.
794 :type dir1_interface: Union[string, int]
795 :type dir2_interface: Union[string, int]
796 :type entry_amount: int
797 :type local_addr_range:
798 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
799 :type remote_addr_range:
800 Union[string, ipaddress.IPv4Address, ipaddress.IPv6Address]
801 :type action: IPsecUtil.PolicyAction
803 :type bidirectional: bool
804 :raises NotImplementedError: When the action is PolicyAction.PROTECT.
807 if action == PolicyAction.PROTECT:
808 raise NotImplementedError('Policy action PROTECT is not supported.')
812 matching_priority = 1
814 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir1)
815 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir1, dir1_interface)
816 # matching entry direction 1
817 IPsecUtil.vpp_ipsec_add_spd_entry(
818 node, spd_id_dir1, matching_priority, action,
819 inbound=inbound, laddr_range=local_addr_range,
820 raddr_range=remote_addr_range
824 IPsecUtil.vpp_ipsec_add_spd(node, spd_id_dir2)
825 IPsecUtil.vpp_ipsec_spd_add_if(node, spd_id_dir2, dir2_interface)
827 # matching entry direction 2, the address ranges are switched
828 IPsecUtil.vpp_ipsec_add_spd_entry(
829 node, spd_id_dir2, matching_priority, action,
830 inbound=inbound, laddr_range=remote_addr_range,
831 raddr_range=local_addr_range
834 # non-matching entries
835 no_match_entry_amount = entry_amount - 1
836 if no_match_entry_amount > 0:
837 # create a NetworkIncrement representation of the network,
838 # then skip the matching network
839 no_match_local_addr_range = NetworkIncrement(
840 ip_network(local_addr_range)
842 next(no_match_local_addr_range)
844 no_match_remote_addr_range = NetworkIncrement(
845 ip_network(remote_addr_range)
847 next(no_match_remote_addr_range)
849 # non-matching entries direction 1
850 IPsecUtil.vpp_ipsec_add_spd_entries(
851 node, no_match_entry_amount, spd_id_dir1,
852 ObjIncrement(matching_priority + 1, 1), action,
853 inbound=inbound, laddr_range=no_match_local_addr_range,
854 raddr_range=no_match_remote_addr_range
858 # reset the networks so that we're using a unified config
859 # the address ranges are switched
860 no_match_remote_addr_range = NetworkIncrement(
861 ip_network(local_addr_range)
863 next(no_match_remote_addr_range)
865 no_match_local_addr_range = NetworkIncrement(
866 ip_network(remote_addr_range)
868 next(no_match_local_addr_range)
869 # non-matching entries direction 2
870 IPsecUtil.vpp_ipsec_add_spd_entries(
871 node, no_match_entry_amount, spd_id_dir2,
872 ObjIncrement(matching_priority + 1, 1), action,
873 inbound=inbound, laddr_range=no_match_local_addr_range,
874 raddr_range=no_match_remote_addr_range
877 IPsecUtil.vpp_ipsec_show_all(node)
880 def vpp_ipsec_add_spd_entry(
881 node, spd_id, priority, action, inbound=True, sa_id=None,
882 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
883 rport_range=None, is_ipv6=False):
884 """Create Security Policy Database entry on the VPP node.
886 :param node: VPP node to add SPD entry on.
887 :param spd_id: SPD ID to add entry on.
888 :param priority: SPD entry priority, higher number = higher priority.
889 :param action: Policy action.
890 :param inbound: If True policy is for inbound traffic, otherwise
892 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
893 :param proto: Policy selector next layer protocol number.
894 :param laddr_range: Policy selector local IPv4 or IPv6 address range
895 in format IP/prefix or IP/mask. If no mask is provided,
896 it's considered to be /32.
897 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
898 in format IP/prefix or IP/mask. If no mask is provided,
899 it's considered to be /32.
900 :param lport_range: Policy selector local TCP/UDP port range in format
901 <port_start>-<port_end>.
902 :param rport_range: Policy selector remote TCP/UDP port range in format
903 <port_start>-<port_end>.
904 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
905 not defined so it will default to address ::/0, otherwise False.
909 :type action: IPsecUtil.PolicyAction
913 :type laddr_range: string
914 :type raddr_range: string
915 :type lport_range: string
916 :type rport_range: string
919 if laddr_range is None:
920 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
922 if raddr_range is None:
923 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
925 local_net = ip_network(laddr_range, strict=False)
926 remote_net = ip_network(raddr_range, strict=False)
928 cmd = u"ipsec_spd_entry_add_del"
929 err_msg = f"Failed to add entry to Security Policy Database " \
930 f"{spd_id} on host {node[u'host']}"
934 priority=int(priority),
935 is_outbound=not inbound,
936 sa_id=int(sa_id) if sa_id else 0,
938 protocol=int(proto) if proto else 0,
939 remote_address_start=IPAddress.create_ip_address_object(
940 remote_net.network_address
942 remote_address_stop=IPAddress.create_ip_address_object(
943 remote_net.broadcast_address
945 local_address_start=IPAddress.create_ip_address_object(
946 local_net.network_address
948 local_address_stop=IPAddress.create_ip_address_object(
949 local_net.broadcast_address
951 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
953 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
955 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
957 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
964 with PapiSocketExecutor(node) as papi_exec:
965 papi_exec.add(cmd, **args).get_reply(err_msg)
968 def vpp_ipsec_add_spd_entries(
969 node, n_entries, spd_id, priority, action, inbound, sa_id=None,
970 proto=None, laddr_range=None, raddr_range=None, lport_range=None,
971 rport_range=None, is_ipv6=False):
972 """Create multiple Security Policy Database entries on the VPP node.
974 :param node: VPP node to add SPD entries on.
975 :param n_entries: Number of SPD entries to be added.
976 :param spd_id: SPD ID to add entries on.
977 :param priority: SPD entries priority, higher number = higher priority.
978 :param action: Policy action.
979 :param inbound: If True policy is for inbound traffic, otherwise
981 :param sa_id: SAD entry ID for action PolicyAction.PROTECT.
982 :param proto: Policy selector next layer protocol number.
983 :param laddr_range: Policy selector local IPv4 or IPv6 address range
984 in format IP/prefix or IP/mask. If no mask is provided,
985 it's considered to be /32.
986 :param raddr_range: Policy selector remote IPv4 or IPv6 address range
987 in format IP/prefix or IP/mask. If no mask is provided,
988 it's considered to be /32.
989 :param lport_range: Policy selector local TCP/UDP port range in format
990 <port_start>-<port_end>.
991 :param rport_range: Policy selector remote TCP/UDP port range in format
992 <port_start>-<port_end>.
993 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
994 not defined so it will default to address ::/0, otherwise False.
998 :type priority: IPsecUtil.ObjIncrement
999 :type action: IPsecUtil.PolicyAction
1001 :type sa_id: IPsecUtil.ObjIncrement
1003 :type laddr_range: IPsecUtil.NetworkIncrement
1004 :type raddr_range: IPsecUtil.NetworkIncrement
1005 :type lport_range: string
1006 :type rport_range: string
1009 if laddr_range is None:
1010 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1011 laddr_range = NetworkIncrement(ip_network(laddr_range), 0)
1013 if raddr_range is None:
1014 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
1015 raddr_range = NetworkIncrement(ip_network(raddr_range), 0)
1017 lport_range_start = 0
1018 lport_range_stop = 65535
1020 lport_range_start, lport_range_stop = lport_range.split('-')
1022 rport_range_start = 0
1023 rport_range_stop = 65535
1025 rport_range_start, rport_range_stop = rport_range.split('-')
1027 if int(n_entries) > 10:
1028 tmp_filename = f"/tmp/ipsec_spd_{spd_id}_add_del_entry.script"
1030 with open(tmp_filename, 'w') as tmp_file:
1031 for _ in range(n_entries):
1032 direction = u'inbound' if inbound else u'outbound'
1033 sa = f' sa {sa_id.inc_fmt()}' if sa_id is not None else ''
1034 protocol = f' protocol {protocol}' if proto else ''
1035 local_port_range = f' local-port-range ' \
1036 f'{lport_range_start} - {lport_range_stop}' \
1037 if lport_range else ''
1038 remote_port_range = f' remote-port-range ' \
1039 f'{rport_range_start} - {rport_range_stop}' \
1040 if rport_range else ''
1042 spd_cfg = f"exec ipsec policy add spd {spd_id} " \
1043 f"priority {priority.inc_fmt()} {direction}" \
1044 f"{protocol} action {action}{sa} " \
1045 f"local-ip-range {laddr_range.inc_fmt()} " \
1046 f"remote-ip-range {raddr_range.inc_fmt()}" \
1047 f"{local_port_range}{remote_port_range}\n"
1049 tmp_file.write(spd_cfg)
1051 VatExecutor().execute_script(
1052 tmp_filename, node, timeout=300, json_out=False,
1053 copy_on_execute=True
1055 os.remove(tmp_filename)
1058 for _ in range(n_entries):
1059 IPsecUtil.vpp_ipsec_add_spd_entry(
1060 node, spd_id, next(priority), action, inbound,
1061 next(sa_id) if sa_id is not None else sa_id,
1062 proto, next(laddr_range), next(raddr_range), lport_range,
1063 rport_range, is_ipv6
1067 def _ipsec_create_tunnel_interfaces_dut1_vat(
1068 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1069 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1070 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
1072 Generate random keys and return them (so DUT2 or TG can decrypt).
1074 :param nodes: VPP nodes to create tunnel interfaces.
1075 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1076 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1077 IPv4/IPv6 address (ip2).
1078 :param if1_key: VPP node 1 interface key from topology file.
1079 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1080 interface key from topology file.
1081 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1082 :param crypto_alg: The encryption algorithm name.
1083 :param integ_alg: The integrity algorithm name.
1084 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1085 first tunnel in direction node2->node1.
1086 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1087 :param addr_incr: IP / IPv6 address incremental step.
1088 :param existing_tunnels: Number of tunnel interfaces before creation.
1089 Useful mainly for reconf tests. Default 0.
1094 :type n_tunnels: int
1095 :type crypto_alg: CryptoAlg
1096 :type integ_alg: Optional[IntegAlg]
1097 :type raddr_ip2: IPv4Address or IPv6Address
1098 :type addr_incr: int
1100 :type existing_tunnels: int
1101 :returns: Generated ckeys and ikeys.
1102 :rtype: List[bytes], List[bytes]
1104 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
1105 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
1107 ckeys = [bytes()] * existing_tunnels
1108 ikeys = [bytes()] * existing_tunnels
1111 with open(tmp_fn1, u"w") as tmp_f1:
1112 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
1113 if u"DUT2" in nodes.keys() \
1114 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
1115 if not existing_tunnels:
1117 f"exec create loopback interface\n"
1118 f"exec set interface state loop0 up\n"
1119 f"exec set interface ip address {if1_n} "
1120 f"{tun_ips[u'ip2'] - 1}/"
1121 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
1122 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
1125 for i in range(existing_tunnels, n_tunnels):
1127 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1130 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1133 integ = f"integ-alg {integ_alg.alg_name} " \
1134 f"integ-key {ikeys[i].hex()} "
1138 f"exec set interface ip address loop0 "
1139 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
1140 f"exec create ipip tunnel "
1141 f"src {tun_ips[u'ip1'] + i * addr_incr} "
1142 f"dst {tun_ips[u'ip2']} "
1144 f"exec ipsec sa add {i} "
1145 f"spi {spi_d[u'spi_1'] + i} "
1146 f"crypto-alg {crypto_alg.alg_name} "
1147 f"crypto-key {ckeys[i].hex()} "
1150 f"exec ipsec sa add {100000 + i} "
1151 f"spi {spi_d[u'spi_2'] + i} "
1152 f"crypto-alg {crypto_alg.alg_name} "
1153 f"crypto-key {ckeys[i].hex()} "
1156 f"exec ipsec tunnel protect ipip{i} "
1158 f"sa-in {100000 + i} "
1162 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1163 copy_on_execute=True,
1164 history=bool(n_tunnels < 100)
1168 with open(tmp_fn1, 'w') as tmp_f1:
1169 for i in range(existing_tunnels, n_tunnels):
1171 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
1172 f"exec set interface state ipip{i} up\n"
1173 f"exec ip route add "
1174 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
1178 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
1179 copy_on_execute=True,
1180 history=bool(n_tunnels < 100)
1187 def _ipsec_create_tunnel_interfaces_dut2_vat(
1188 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1189 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1190 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
1192 This method accesses keys generated by DUT1 method
1193 and does not return anything.
1195 :param nodes: VPP nodes to create tunnel interfaces.
1196 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1197 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1198 IPv4/IPv6 address (ip2).
1199 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1200 interface key from topology file.
1201 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1202 :param crypto_alg: The encryption algorithm name.
1203 :param ckeys: List of encryption keys.
1204 :param integ_alg: The integrity algorithm name.
1205 :param ikeys: List of integrity keys.
1206 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1207 :param addr_incr: IP / IPv6 address incremental step.
1208 :param existing_tunnels: Number of tunnel interfaces before creation.
1209 Useful mainly for reconf tests. Default 0.
1213 :type n_tunnels: int
1214 :type crypto_alg: CryptoAlg
1215 :type ckeys: Sequence[bytes]
1216 :type integ_alg: Optional[IntegAlg]
1217 :type ikeys: Sequence[bytes]
1218 :type addr_incr: int
1220 :type existing_tunnels: int
1222 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1223 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1226 with open(tmp_fn2, 'w') as tmp_f2:
1227 if not existing_tunnels:
1229 f"exec set interface ip address {if2_n}"
1230 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1232 for i in range(existing_tunnels, n_tunnels):
1234 integ = f"integ-alg {integ_alg.alg_name} " \
1235 f"integ-key {ikeys[i].hex()} "
1239 f"exec create ipip tunnel "
1240 f"src {tun_ips[u'ip2']} "
1241 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1243 f"exec ipsec sa add {100000 + i} "
1244 f"spi {spi_d[u'spi_2'] + i} "
1245 f"crypto-alg {crypto_alg.alg_name} "
1246 f"crypto-key {ckeys[i].hex()} "
1249 f"exec ipsec sa add {i} "
1250 f"spi {spi_d[u'spi_1'] + i} "
1251 f"crypto-alg {crypto_alg.alg_name} "
1252 f"crypto-key {ckeys[i].hex()} "
1255 f"exec ipsec tunnel protect ipip{i} "
1256 f"sa-out {100000 + i} "
1261 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1262 copy_on_execute=True,
1263 history=bool(n_tunnels < 100)
1267 with open(tmp_fn2, 'w') as tmp_f2:
1268 if not existing_tunnels:
1270 f"exec ip route add {tun_ips[u'ip1']}/8 "
1271 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1273 for i in range(existing_tunnels, n_tunnels):
1275 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1276 f"exec set interface state ipip{i} up\n"
1277 f"exec ip route add "
1278 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1282 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1283 copy_on_execute=True,
1284 history=bool(n_tunnels < 100)
1289 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1290 """Create loopback interface and set IP address on VPP node 1 interface
1293 :param nodes: VPP nodes to create tunnel interfaces.
1294 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1295 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1296 IPv4/IPv6 address (ip2).
1297 :param if1_key: VPP node 1 interface key from topology file.
1298 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1299 interface key from topology file.
1305 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1306 # Create loopback interface on DUT1, set it to up state
1307 cmd = u"create_loopback_instance"
1313 err_msg = f"Failed to create loopback interface " \
1314 f"on host {nodes[u'DUT1'][u'host']}"
1315 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1316 get_sw_if_index(err_msg)
1317 cmd = u"sw_interface_set_flags"
1319 sw_if_index=loop_sw_if_idx,
1320 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1322 err_msg = f"Failed to set loopback interface state up " \
1323 f"on host {nodes[u'DUT1'][u'host']}"
1324 papi_exec.add(cmd, **args).get_reply(err_msg)
1325 # Set IP address on VPP node 1 interface
1326 cmd = u"sw_interface_add_del_address"
1328 sw_if_index=InterfaceUtil.get_interface_index(
1329 nodes[u"DUT1"], if1_key
1333 prefix=IPUtil.create_prefix_object(
1334 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1338 err_msg = f"Failed to set IP address on interface {if1_key} " \
1339 f"on host {nodes[u'DUT1'][u'host']}"
1340 papi_exec.add(cmd, **args).get_reply(err_msg)
1341 cmd2 = u"ip_neighbor_add_del"
1345 sw_if_index=Topology.get_interface_sw_index(
1346 nodes[u"DUT1"], if1_key
1350 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1351 if u"DUT2" in nodes.keys()
1352 else Topology.get_interface_mac(
1353 nodes[u"TG"], if2_key
1356 ip_address=tun_ips[u"ip2"].compressed
1359 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1360 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1362 return loop_sw_if_idx
1365 def _ipsec_create_tunnel_interfaces_dut1_papi(
1366 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1367 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1368 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1370 Generate random keys and return them (so DUT2 or TG can decrypt).
1372 :param nodes: VPP nodes to create tunnel interfaces.
1373 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1374 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1375 IPv4/IPv6 address (ip2).
1376 :param if1_key: VPP node 1 interface key from topology file.
1377 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1378 interface key from topology file.
1379 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1380 :param crypto_alg: The encryption algorithm name.
1381 :param integ_alg: The integrity algorithm name.
1382 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1383 first tunnel in direction node2->node1.
1384 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1385 :param addr_incr: IP / IPv6 address incremental step.
1386 :param existing_tunnels: Number of tunnel interfaces before creation.
1387 Useful mainly for reconf tests. Default 0.
1392 :type n_tunnels: int
1393 :type crypto_alg: CryptoAlg
1394 :type integ_alg: Optional[IntegAlg]
1395 :type raddr_ip2: IPv4Address or IPv6Address
1396 :type addr_incr: int
1398 :type existing_tunnels: int
1399 :returns: Generated ckeys and ikeys.
1400 :rtype: List[bytes], List[bytes]
1402 if not existing_tunnels:
1403 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1404 nodes, tun_ips, if1_key, if2_key
1407 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1408 nodes[u"DUT1"], u"loop0"
1410 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1411 # Configure IP addresses on loop0 interface
1412 cmd = u"sw_interface_add_del_address"
1414 sw_if_index=loop_sw_if_idx,
1419 for i in range(existing_tunnels, n_tunnels):
1420 args[u"prefix"] = IPUtil.create_prefix_object(
1421 tun_ips[u"ip1"] + i * addr_incr,
1422 128 if tun_ips[u"ip1"].version == 6 else 32
1425 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1427 # Configure IPIP tunnel interfaces
1428 cmd = u"ipip_add_tunnel"
1430 instance=Constants.BITWISE_NON_ZERO,
1435 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1437 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1438 dscp=int(IpDscp.IP_API_DSCP_CS0)
1443 ipip_tunnels = [None] * existing_tunnels
1444 for i in range(existing_tunnels, n_tunnels):
1445 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1446 tun_ips[u"ip1"] + i * addr_incr
1448 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1452 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1454 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1455 f" {nodes[u'DUT1'][u'host']}"
1456 ipip_tunnels.extend(
1458 reply[u"sw_if_index"]
1459 for reply in papi_exec.get_replies(err_msg)
1460 if u"sw_if_index" in reply
1463 # Configure IPSec SAD entries
1464 ckeys = [bytes()] * existing_tunnels
1465 ikeys = [bytes()] * existing_tunnels
1466 cmd = u"ipsec_sad_entry_add_del_v3"
1478 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1479 crypto_algorithm=crypto_alg.alg_int_repr,
1481 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1482 integrity_key=i_key,
1488 encap_decap_flags=int(
1489 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1491 dscp=int(IpDscp.IP_API_DSCP_CS0),
1494 udp_src_port=IPSEC_UDP_PORT_NONE,
1495 udp_dst_port=IPSEC_UDP_PORT_NONE,
1501 for i in range(existing_tunnels, n_tunnels):
1503 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1506 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1508 # SAD entry for outband / tx path
1509 args[u"entry"][u"sad_id"] = i
1510 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1512 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1513 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1515 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1516 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1517 args[u"entry"][u"flags"] = int(
1518 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1521 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1523 # SAD entry for inband / rx path
1524 args[u"entry"][u"sad_id"] = 100000 + i
1525 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1527 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1528 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1530 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1531 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1532 args[u"entry"][u"flags"] = int(
1533 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1534 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1537 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1539 err_msg = f"Failed to add IPsec SAD entries on host" \
1540 f" {nodes[u'DUT1'][u'host']}"
1541 papi_exec.get_replies(err_msg)
1542 # Add protection for tunnels with IPSEC
1543 cmd = u"ipsec_tunnel_protect_update"
1546 via_label=MPLS_LABEL_INVALID,
1547 obj_id=Constants.BITWISE_NON_ZERO
1549 ipsec_tunnel_protect = dict(
1557 tunnel=ipsec_tunnel_protect
1559 for i in range(existing_tunnels, n_tunnels):
1560 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1561 args[u"tunnel"][u"sa_out"] = i
1562 args[u"tunnel"][u"sa_in"] = [100000 + i]
1564 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1566 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1567 f"on host {nodes[u'DUT1'][u'host']}"
1568 papi_exec.get_replies(err_msg)
1570 # Configure unnumbered interfaces
1571 cmd = u"sw_interface_set_unnumbered"
1574 sw_if_index=InterfaceUtil.get_interface_index(
1575 nodes[u"DUT1"], if1_key
1577 unnumbered_sw_if_index=0
1579 for i in range(existing_tunnels, n_tunnels):
1580 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1582 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1585 cmd = u"sw_interface_set_flags"
1588 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1590 for i in range(existing_tunnels, n_tunnels):
1591 args[u"sw_if_index"] = ipip_tunnels[i]
1593 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1595 # Configure IP routes
1596 cmd = u"ip_route_add_del"
1602 for i in range(existing_tunnels, n_tunnels):
1603 args[u"route"] = IPUtil.compose_vpp_route_structure(
1604 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1605 prefix_len=128 if raddr_ip2.version == 6 else 32,
1606 interface=ipip_tunnels[i]
1609 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1611 err_msg = f"Failed to add IP routes on host " \
1612 f"{nodes[u'DUT1'][u'host']}"
1613 papi_exec.get_replies(err_msg)
1618 def _ipsec_create_tunnel_interfaces_dut2_papi(
1619 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1620 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1621 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1623 This method accesses keys generated by DUT1 method
1624 and does not return anything.
1626 :param nodes: VPP nodes to create tunnel interfaces.
1627 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1628 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1629 IPv4/IPv6 address (ip2).
1630 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1631 interface key from topology file.
1632 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1633 :param crypto_alg: The encryption algorithm name.
1634 :param ckeys: List of encryption keys.
1635 :param integ_alg: The integrity algorithm name.
1636 :param ikeys: List of integrity keys.
1637 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1638 :param addr_incr: IP / IPv6 address incremental step.
1639 :param existing_tunnels: Number of tunnel interfaces before creation.
1640 Useful mainly for reconf tests. Default 0.
1644 :type n_tunnels: int
1645 :type crypto_alg: CryptoAlg
1646 :type ckeys: Sequence[bytes]
1647 :type integ_alg: Optional[IntegAlg]
1648 :type ikeys: Sequence[bytes]
1649 :type addr_incr: int
1651 :type existing_tunnels: int
1653 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1654 if not existing_tunnels:
1655 # Set IP address on VPP node 2 interface
1656 cmd = u"sw_interface_add_del_address"
1658 sw_if_index=InterfaceUtil.get_interface_index(
1659 nodes[u"DUT2"], if2_key
1663 prefix=IPUtil.create_prefix_object(
1664 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1668 err_msg = f"Failed to set IP address on interface {if2_key} " \
1669 f"on host {nodes[u'DUT2'][u'host']}"
1670 papi_exec.add(cmd, **args).get_reply(err_msg)
1671 # Configure IPIP tunnel interfaces
1672 cmd = u"ipip_add_tunnel"
1674 instance=Constants.BITWISE_NON_ZERO,
1679 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1681 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1682 dscp=int(IpDscp.IP_API_DSCP_CS0)
1687 ipip_tunnels = [None] * existing_tunnels
1688 for i in range(existing_tunnels, n_tunnels):
1689 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1692 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1693 tun_ips[u"ip1"] + i * addr_incr
1696 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1698 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1699 f" {nodes[u'DUT2'][u'host']}"
1700 ipip_tunnels.extend(
1702 reply[u"sw_if_index"]
1703 for reply in papi_exec.get_replies(err_msg)
1704 if u"sw_if_index" in reply
1707 # Configure IPSec SAD entries
1708 cmd = u"ipsec_sad_entry_add_del_v3"
1720 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1721 crypto_algorithm=crypto_alg.alg_int_repr,
1723 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1724 integrity_key=i_key,
1730 encap_decap_flags=int(
1731 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1733 dscp=int(IpDscp.IP_API_DSCP_CS0),
1736 udp_src_port=IPSEC_UDP_PORT_NONE,
1737 udp_dst_port=IPSEC_UDP_PORT_NONE,
1743 for i in range(existing_tunnels, n_tunnels):
1745 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1748 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1750 # SAD entry for outband / tx path
1751 args[u"entry"][u"sad_id"] = 100000 + i
1752 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1754 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1755 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1757 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1758 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1759 args[u"entry"][u"flags"] = int(
1760 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1763 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1765 # SAD entry for inband / rx path
1766 args[u"entry"][u"sad_id"] = i
1767 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1769 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1770 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1772 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1773 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1774 args[u"entry"][u"flags"] = int(
1775 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1776 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1779 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1781 err_msg = f"Failed to add IPsec SAD entries on host" \
1782 f" {nodes[u'DUT2'][u'host']}"
1783 papi_exec.get_replies(err_msg)
1784 # Add protection for tunnels with IPSEC
1785 cmd = u"ipsec_tunnel_protect_update"
1788 via_label=MPLS_LABEL_INVALID,
1789 obj_id=Constants.BITWISE_NON_ZERO
1791 ipsec_tunnel_protect = dict(
1799 tunnel=ipsec_tunnel_protect
1801 for i in range(existing_tunnels, n_tunnels):
1802 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1803 args[u"tunnel"][u"sa_out"] = 100000 + i
1804 args[u"tunnel"][u"sa_in"] = [i]
1806 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1808 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1809 f"on host {nodes[u'DUT2'][u'host']}"
1810 papi_exec.get_replies(err_msg)
1812 if not existing_tunnels:
1813 # Configure IP route
1814 cmd = u"ip_route_add_del"
1815 route = IPUtil.compose_vpp_route_structure(
1816 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1817 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1819 gateway=(tun_ips[u"ip2"] - 1).compressed
1826 papi_exec.add(cmd, **args)
1827 # Configure unnumbered interfaces
1828 cmd = u"sw_interface_set_unnumbered"
1831 sw_if_index=InterfaceUtil.get_interface_index(
1832 nodes[u"DUT2"], if2_key
1834 unnumbered_sw_if_index=0
1836 for i in range(existing_tunnels, n_tunnels):
1837 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1839 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1842 cmd = u"sw_interface_set_flags"
1845 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1847 for i in range(existing_tunnels, n_tunnels):
1848 args[u"sw_if_index"] = ipip_tunnels[i]
1850 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1852 # Configure IP routes
1853 cmd = u"ip_route_add_del"
1859 for i in range(existing_tunnels, n_tunnels):
1860 args[u"route"] = IPUtil.compose_vpp_route_structure(
1861 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1862 prefix_len=128 if raddr_ip1.version == 6 else 32,
1863 interface=ipip_tunnels[i]
1866 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1868 err_msg = f"Failed to add IP routes " \
1869 f"on host {nodes[u'DUT2'][u'host']}"
1870 papi_exec.get_replies(err_msg)
1873 def vpp_ipsec_create_tunnel_interfaces(
1874 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1875 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1876 existing_tunnels=0, return_keys=False):
1877 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1879 Some deployments (e.g. devicetest) need to know the generated keys.
1880 But other deployments (e.g. scale perf test) would get spammed
1881 if we returned keys every time.
1883 :param nodes: VPP nodes to create tunnel interfaces.
1884 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1886 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1888 :param if1_key: VPP node 1 interface key from topology file.
1889 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1890 interface key from topology file.
1891 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1892 :param crypto_alg: The encryption algorithm name.
1893 :param integ_alg: The integrity algorithm name.
1894 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1895 first tunnel in direction node1->node2.
1896 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1897 first tunnel in direction node2->node1.
1898 :param raddr_range: Mask specifying range of Policy selector Remote
1899 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1900 and to 128 in case of IPv6.
1901 :param existing_tunnels: Number of tunnel interfaces before creation.
1902 Useful mainly for reconf tests. Default 0.
1903 :param return_keys: Whether generated keys should be returned.
1905 :type tun_if1_ip_addr: str
1906 :type tun_if2_ip_addr: str
1909 :type n_tunnels: int
1910 :type crypto_alg: CryptoAlg
1911 :type integ_alg: Optonal[IntegAlg]
1912 :type raddr_ip1: string
1913 :type raddr_ip2: string
1914 :type raddr_range: int
1915 :type existing_tunnels: int
1916 :type return_keys: bool
1917 :returns: Ckeys, ikeys, spi_1, spi_2.
1918 :rtype: Optional[List[bytes], List[bytes], int, int]
1920 n_tunnels = int(n_tunnels)
1921 existing_tunnels = int(existing_tunnels)
1927 ip1=ip_address(tun_if1_ip_addr),
1928 ip2=ip_address(tun_if2_ip_addr)
1930 raddr_ip1 = ip_address(raddr_ip1)
1931 raddr_ip2 = ip_address(raddr_ip2)
1932 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1933 else 1 << (32 - raddr_range)
1935 if n_tunnels - existing_tunnels > 10:
1936 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1937 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1938 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1940 if u"DUT2" in nodes.keys():
1941 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1942 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1943 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1947 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1948 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1949 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1951 if u"DUT2" in nodes.keys():
1952 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1953 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1954 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1959 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1963 def _create_ipsec_script_files(dut, instances):
1964 """Create script files for configuring IPsec in containers
1966 :param dut: DUT node on which to create the script files
1967 :param instances: number of containers on DUT node
1969 :type instances: int
1972 for cnf in range(0, instances):
1974 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1976 scripts.append(open(script_filename, 'w'))
1980 def _close_and_copy_ipsec_script_files(
1981 dut, nodes, instances, scripts):
1982 """Close created scripts and copy them to containers
1984 :param dut: DUT node on which to create the script files
1985 :param nodes: VPP nodes
1986 :param instances: number of containers on DUT node
1987 :param scripts: dictionary holding the script files
1990 :type instances: int
1993 for cnf in range(0, instances):
1994 scripts[cnf].close()
1996 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1998 scp_node(nodes[dut], script_filename, script_filename)
2002 def vpp_ipsec_create_tunnel_interfaces_in_containers(
2003 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
2004 raddr_ip1, raddr_ip2, raddr_range, n_instances):
2005 """Create multiple IPsec tunnel interfaces between two VPP nodes.
2007 :param nodes: VPP nodes to create tunnel interfaces.
2008 :param if1_ip_addr: VPP node 1 interface IP4 address.
2009 :param if2_ip_addr: VPP node 2 interface IP4 address.
2010 :param n_tunnels: Number of tunnell interfaces to create.
2011 :param crypto_alg: The encryption algorithm name.
2012 :param integ_alg: The integrity algorithm name.
2013 :param raddr_ip1: Policy selector remote IPv4 start address for the
2014 first tunnel in direction node1->node2.
2015 :param raddr_ip2: Policy selector remote IPv4 start address for the
2016 first tunnel in direction node2->node1.
2017 :param raddr_range: Mask specifying range of Policy selector Remote
2018 IPv4 addresses. Valid values are from 1 to 32.
2019 :param n_instances: Number of containers.
2021 :type if1_ip_addr: str
2022 :type if2_ip_addr: str
2023 :type n_tunnels: int
2024 :type crypto_alg: CryptoAlg
2025 :type integ_alg: Optional[IntegAlg]
2026 :type raddr_ip1: string
2027 :type raddr_ip2: string
2028 :type raddr_range: int
2029 :type n_instances: int
2033 addr_incr = 1 << (32 - raddr_range)
2035 dut1_scripts = IPsecUtil._create_ipsec_script_files(
2036 u"DUT1", n_instances
2038 dut2_scripts = IPsecUtil._create_ipsec_script_files(
2039 u"DUT2", n_instances
2042 for cnf in range(0, n_instances):
2043 dut1_scripts[cnf].write(
2044 u"create loopback interface\n"
2045 u"set interface state loop0 up\n\n"
2047 dut2_scripts[cnf].write(
2048 f"ip route add {if1_ip_addr}/8 via "
2049 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
2052 for tnl in range(0, n_tunnels):
2053 cnf = tnl % n_instances
2055 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
2059 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
2063 f"integ-alg {integ_alg.alg_name} "
2064 f"local-integ-key {ikey} "
2065 f"remote-integ-key {ikey} "
2067 # Configure tunnel end point(s) on left side
2068 dut1_scripts[cnf].write(
2069 u"set interface ip address loop0 "
2070 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
2071 f"create ipsec tunnel "
2072 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2073 f"local-spi {spi_1 + tnl} "
2074 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
2075 f"remote-spi {spi_2 + tnl} "
2076 f"crypto-alg {crypto_alg.alg_name} "
2077 f"local-crypto-key {ckey} "
2078 f"remote-crypto-key {ckey} "
2079 f"instance {tnl // n_instances} "
2082 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
2083 f"set interface state ipip{tnl // n_instances} up\n"
2084 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
2085 f"via ipip{tnl // n_instances}\n\n"
2087 # Configure tunnel end point(s) on right side
2088 dut2_scripts[cnf].write(
2089 f"set ip neighbor memif1/{cnf + 1} "
2090 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
2091 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
2092 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
2093 f"local-spi {spi_2 + tnl} "
2094 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
2095 f"remote-spi {spi_1 + tnl} "
2096 f"crypto-alg {crypto_alg.alg_name} "
2097 f"local-crypto-key {ckey} "
2098 f"remote-crypto-key {ckey} "
2099 f"instance {tnl // n_instances} "
2102 f"set interface unnumbered ipip{tnl // n_instances} "
2103 f"use memif1/{cnf + 1}\n"
2104 f"set interface state ipip{tnl // n_instances} up\n"
2105 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
2106 f"via ipip{tnl // n_instances}\n\n"
2109 IPsecUtil._close_and_copy_ipsec_script_files(
2110 u"DUT1", nodes, n_instances, dut1_scripts)
2111 IPsecUtil._close_and_copy_ipsec_script_files(
2112 u"DUT2", nodes, n_instances, dut2_scripts)
2115 def vpp_ipsec_add_multiple_tunnels(
2116 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
2117 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
2118 """Create multiple IPsec tunnels between two VPP nodes.
2120 :param nodes: VPP nodes to create tunnels.
2121 :param interface1: Interface name or sw_if_index on node 1.
2122 :param interface2: Interface name or sw_if_index on node 2.
2123 :param n_tunnels: Number of tunnels to create.
2124 :param crypto_alg: The encryption algorithm name.
2125 :param integ_alg: The integrity algorithm name.
2126 :param tunnel_ip1: Tunnel node1 IPv4 address.
2127 :param tunnel_ip2: Tunnel node2 IPv4 address.
2128 :param raddr_ip1: Policy selector remote IPv4 start address for the
2129 first tunnel in direction node1->node2.
2130 :param raddr_ip2: Policy selector remote IPv4 start address for the
2131 first tunnel in direction node2->node1.
2132 :param raddr_range: Mask specifying range of Policy selector Remote
2133 IPv4 addresses. Valid values are from 1 to 32.
2135 :type interface1: str or int
2136 :type interface2: str or int
2137 :type n_tunnels: int
2138 :type crypto_alg: CryptoAlg
2139 :type integ_alg: Optional[IntegAlg]
2140 :type tunnel_ip1: str
2141 :type tunnel_ip2: str
2142 :type raddr_ip1: string
2143 :type raddr_ip2: string
2144 :type raddr_range: int
2153 dut1_local_outbound_range = ip_network(f"{tunnel_ip1}/8", False).\
2155 dut1_remote_outbound_range = ip_network(f"{tunnel_ip2}/8", False).\
2158 crypto_key = gen_key(
2159 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
2161 integ_key = gen_key(
2162 IPsecUtil.get_integ_alg_key_len(integ_alg)
2163 ).decode() if integ_alg else u""
2165 rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
2166 if u"DUT2" in nodes.keys() \
2167 else Topology.get_interface_mac(nodes[u"TG"], interface2)
2168 IPsecUtil.vpp_ipsec_set_ip_route(
2169 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
2170 interface1, raddr_range, rmac)
2172 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
2173 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
2174 IPsecUtil.vpp_ipsec_add_spd_entry(
2175 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
2176 proto=50, laddr_range=dut1_local_outbound_range,
2177 raddr_range=dut1_remote_outbound_range
2179 IPsecUtil.vpp_ipsec_add_spd_entry(
2180 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
2181 proto=50, laddr_range=dut1_remote_outbound_range,
2182 raddr_range=dut1_local_outbound_range
2185 IPsecUtil.vpp_ipsec_add_sad_entries(
2186 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
2187 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
2190 IPsecUtil.vpp_ipsec_add_spd_entries(
2191 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2192 action=PolicyAction.PROTECT, inbound=False,
2193 sa_id=ObjIncrement(sa_id_1, 1),
2194 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2197 IPsecUtil.vpp_ipsec_add_sad_entries(
2198 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
2199 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2201 IPsecUtil.vpp_ipsec_add_spd_entries(
2202 nodes[u"DUT1"], n_tunnels, spd_id, priority=ObjIncrement(p_lo, 0),
2203 action=PolicyAction.PROTECT, inbound=True,
2204 sa_id=ObjIncrement(sa_id_2, 1),
2205 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2208 if u"DUT2" in nodes.keys():
2209 rmac = Topology.get_interface_mac(nodes[u"DUT1"], interface1)
2210 IPsecUtil.vpp_ipsec_set_ip_route(
2211 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2212 interface2, raddr_range, rmac)
2214 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2215 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2216 IPsecUtil.vpp_ipsec_add_spd_entry(
2217 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2218 inbound=False, proto=50, laddr_range=dut1_remote_outbound_range,
2219 raddr_range=dut1_local_outbound_range
2221 IPsecUtil.vpp_ipsec_add_spd_entry(
2222 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2223 inbound=True, proto=50, laddr_range=dut1_local_outbound_range,
2224 raddr_range=dut1_remote_outbound_range
2227 IPsecUtil.vpp_ipsec_add_sad_entries(
2228 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2229 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2
2231 IPsecUtil.vpp_ipsec_add_spd_entries(
2232 nodes[u"DUT2"], n_tunnels, spd_id,
2233 priority=ObjIncrement(p_lo, 0),
2234 action=PolicyAction.PROTECT, inbound=True,
2235 sa_id=ObjIncrement(sa_id_1, 1),
2236 raddr_range=NetworkIncrement(ip_network(raddr_ip2))
2239 IPsecUtil.vpp_ipsec_add_sad_entries(
2240 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2241 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2243 IPsecUtil.vpp_ipsec_add_spd_entries(
2244 nodes[u"DUT2"], n_tunnels, spd_id,
2245 priority=ObjIncrement(p_lo, 0),
2246 action=PolicyAction.PROTECT, inbound=False,
2247 sa_id=ObjIncrement(sa_id_2, 1),
2248 raddr_range=NetworkIncrement(ip_network(raddr_ip1))
2252 def vpp_ipsec_show_all(node):
2253 """Run "show ipsec all" debug CLI command.
2255 :param node: Node to run command on.
2258 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec all")
2261 def show_ipsec_security_association(node):
2262 """Show IPSec security association.
2264 :param node: DUT node.
2270 PapiSocketExecutor.dump_and_log(node, cmds)