1 # Copyright (c) 2021 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """IPsec utilities library."""
18 from enum import Enum, IntEnum
20 from random import choice
21 from string import ascii_letters
23 from ipaddress import ip_network, ip_address
25 from resources.libraries.python.Constants import Constants
26 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
28 from resources.libraries.python.IPAddress import IPAddress
29 from resources.libraries.python.IPUtil import IPUtil, IpDscp, MPLS_LABEL_INVALID
30 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
31 from resources.libraries.python.ssh import scp_node
32 from resources.libraries.python.topology import Topology
33 from resources.libraries.python.VatExecutor import VatExecutor
36 IPSEC_UDP_PORT_NONE = 0xffff
40 """Generate random string as a key.
42 :param length: Length of generated payload.
44 :returns: The generated payload.
48 choice(ascii_letters) for _ in range(length)
49 ).encode(encoding=u"utf-8")
52 class PolicyAction(Enum):
54 BYPASS = (u"bypass", 0)
55 DISCARD = (u"discard", 1)
56 PROTECT = (u"protect", 3)
58 def __init__(self, policy_name, policy_int_repr):
59 self.policy_name = policy_name
60 self.policy_int_repr = policy_int_repr
63 class CryptoAlg(Enum):
64 """Encryption algorithms."""
65 AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
66 AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
67 AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
68 AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
70 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
71 self.alg_name = alg_name
72 self.alg_int_repr = alg_int_repr
73 self.scapy_name = scapy_name
74 self.key_len = key_len
78 """Integrity algorithm."""
79 SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
80 SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
82 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
83 self.alg_name = alg_name
84 self.alg_int_repr = alg_int_repr
85 self.scapy_name = scapy_name
86 self.key_len = key_len
89 class IPsecProto(IntEnum):
91 IPSEC_API_PROTO_ESP = 50
92 IPSEC_API_PROTO_AH = 51
95 class IPsecSadFlags(IntEnum):
96 """IPsec Security Association Database flags."""
97 IPSEC_API_SAD_FLAG_NONE = 0,
98 # Enable extended sequence numbers
99 IPSEC_API_SAD_FLAG_USE_ESN = 0x01,
100 # Enable Anti - replay
101 IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02,
102 # IPsec tunnel mode if non-zero, else transport mode
103 IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04,
104 # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
105 # only valid if is_tunnel is non-zero
106 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08,
107 # Enable UDP encapsulation for NAT traversal
108 IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10,
109 # IPsec SA is or inbound traffic
110 IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
113 class TunnelEncpaDecapFlags(IntEnum):
114 """Flags controlling tunnel behaviour."""
115 TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
116 # at encap, copy the DF bit of the payload into the tunnel header
117 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
118 # at encap, set the DF bit in the tunnel header
119 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
120 # at encap, copy the DSCP bits of the payload into the tunnel header
121 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
122 # at encap, copy the ECN bit of the payload into the tunnel header
123 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
124 # at decap, copy the ECN bit of the tunnel header into the payload
125 TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
128 class TunnelMode(IntEnum):
131 TUNNEL_API_MODE_P2P = 0
133 TUNNEL_API_MODE_MP = 1
137 """IPsec utilities."""
140 def policy_action_bypass():
141 """Return policy action bypass.
143 :returns: PolicyAction enum BYPASS object.
146 return PolicyAction.BYPASS
149 def policy_action_discard():
150 """Return policy action discard.
152 :returns: PolicyAction enum DISCARD object.
155 return PolicyAction.DISCARD
158 def policy_action_protect():
159 """Return policy action protect.
161 :returns: PolicyAction enum PROTECT object.
164 return PolicyAction.PROTECT
167 def crypto_alg_aes_cbc_128():
168 """Return encryption algorithm aes-cbc-128.
170 :returns: CryptoAlg enum AES_CBC_128 object.
173 return CryptoAlg.AES_CBC_128
176 def crypto_alg_aes_cbc_256():
177 """Return encryption algorithm aes-cbc-256.
179 :returns: CryptoAlg enum AES_CBC_256 object.
182 return CryptoAlg.AES_CBC_256
185 def crypto_alg_aes_gcm_128():
186 """Return encryption algorithm aes-gcm-128.
188 :returns: CryptoAlg enum AES_GCM_128 object.
191 return CryptoAlg.AES_GCM_128
194 def crypto_alg_aes_gcm_256():
195 """Return encryption algorithm aes-gcm-256.
197 :returns: CryptoAlg enum AES_GCM_128 object.
200 return CryptoAlg.AES_GCM_256
203 def get_crypto_alg_key_len(crypto_alg):
204 """Return encryption algorithm key length.
206 :param crypto_alg: Encryption algorithm.
207 :type crypto_alg: CryptoAlg
208 :returns: Key length.
211 return crypto_alg.key_len
214 def get_crypto_alg_scapy_name(crypto_alg):
215 """Return encryption algorithm scapy name.
217 :param crypto_alg: Encryption algorithm.
218 :type crypto_alg: CryptoAlg
219 :returns: Algorithm scapy name.
222 return crypto_alg.scapy_name
225 def integ_alg_sha_256_128():
226 """Return integrity algorithm SHA-256-128.
228 :returns: IntegAlg enum SHA_256_128 object.
231 return IntegAlg.SHA_256_128
234 def integ_alg_sha_512_256():
235 """Return integrity algorithm SHA-512-256.
237 :returns: IntegAlg enum SHA_512_256 object.
240 return IntegAlg.SHA_512_256
243 def get_integ_alg_key_len(integ_alg):
244 """Return integrity algorithm key length.
246 :param integ_alg: Integrity algorithm.
247 :type integ_alg: IntegAlg
248 :returns: Key length.
251 return integ_alg.key_len
254 def get_integ_alg_scapy_name(integ_alg):
255 """Return integrity algorithm scapy name.
257 :param integ_alg: Integrity algorithm.
258 :type integ_alg: IntegAlg
259 :returns: Algorithm scapy name.
262 return integ_alg.scapy_name
265 def ipsec_proto_esp():
266 """Return IPSec protocol ESP.
268 :returns: IPsecProto enum ESP object.
271 return int(IPsecProto.IPSEC_API_PROTO_ESP)
274 def ipsec_proto_ah():
275 """Return IPSec protocol AH.
277 :returns: IPsecProto enum AH object.
280 return int(IPsecProto.IPSEC_API_PROTO_AH)
283 def vpp_ipsec_select_backend(node, protocol, index=1):
284 """Select IPsec backend.
286 :param node: VPP node to select IPsec backend on.
287 :param protocol: IPsec protocol.
288 :param index: Backend index.
290 :type protocol: IPsecProto
292 :raises RuntimeError: If failed to select IPsec backend or if no API
295 cmd = u"ipsec_select_backend"
296 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
301 with PapiSocketExecutor(node) as papi_exec:
302 papi_exec.add(cmd, **args).get_reply(err_msg)
305 def vpp_ipsec_set_async_mode(node, async_enable=1):
306 """Set IPsec async mode on|off.
308 :param node: VPP node to set IPsec async mode.
309 :param async_enable: Async mode on or off.
311 :type async_enable: int
312 :raises RuntimeError: If failed to set IPsec async mode or if no API
315 cmd = u"ipsec_set_async_mode"
316 err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
318 async_enable=async_enable
320 with PapiSocketExecutor(node) as papi_exec:
321 papi_exec.add(cmd, **args).get_reply(err_msg)
324 def vpp_ipsec_crypto_sw_scheduler_set_worker(
325 node, worker_index, crypto_enable=False):
326 """Enable or disable crypto on specific vpp worker threads.
328 :param node: VPP node to enable or disable crypto for worker threads.
329 :param worker_index: VPP worker thread index.
330 :param crypto_enable: Disable or enable crypto work.
332 :type worker_index: int
333 :type crypto_enable: bool
334 :raises RuntimeError: If failed to enable or disable crypto for worker
335 thread or if no API reply received.
337 cmd = u"crypto_sw_scheduler_set_worker"
338 err_msg = f"Failed to disable/enable crypto for worker thread " \
339 f"on host {node[u'host']}"
341 worker_index=worker_index,
342 crypto_enable=crypto_enable
344 with PapiSocketExecutor(node) as papi_exec:
345 papi_exec.add(cmd, **args).get_reply(err_msg)
348 def vpp_ipsec_add_sad_entry(
349 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
350 integ_key=u"", tunnel_src=None, tunnel_dst=None):
351 """Create Security Association Database entry on the VPP node.
353 :param node: VPP node to add SAD entry on.
354 :param sad_id: SAD entry ID.
355 :param spi: Security Parameter Index of this SAD entry.
356 :param crypto_alg: The encryption algorithm name.
357 :param crypto_key: The encryption key string.
358 :param integ_alg: The integrity algorithm name.
359 :param integ_key: The integrity key string.
360 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
361 specified ESP transport mode is used.
362 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
363 not specified ESP transport mode is used.
367 :type crypto_alg: CryptoAlg
368 :type crypto_key: str
369 :type integ_alg: IntegAlg
371 :type tunnel_src: str
372 :type tunnel_dst: str
374 if isinstance(crypto_key, str):
375 crypto_key = crypto_key.encode(encoding=u"utf-8")
376 if isinstance(integ_key, str):
377 integ_key = integ_key.encode(encoding=u"utf-8")
379 length=len(crypto_key),
383 length=len(integ_key),
384 data=integ_key if integ_key else 0
387 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
388 if tunnel_src and tunnel_dst:
389 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
390 src_addr = ip_address(tunnel_src)
391 dst_addr = ip_address(tunnel_dst)
392 if src_addr.version == 6:
394 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
399 cmd = u"ipsec_sad_entry_add_del_v2"
400 err_msg = f"Failed to add Security Association Database entry " \
401 f"on host {node[u'host']}"
405 crypto_algorithm=crypto_alg.alg_int_repr,
407 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
410 tunnel_src=str(src_addr),
411 tunnel_dst=str(dst_addr),
413 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
415 dscp=int(IpDscp.IP_API_DSCP_CS0),
416 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
417 udp_src_port=4500, # default value in api
418 udp_dst_port=4500 # default value in api
424 with PapiSocketExecutor(node) as papi_exec:
425 papi_exec.add(cmd, **args).get_reply(err_msg)
428 def vpp_ipsec_add_sad_entries(
429 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
430 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
431 """Create multiple Security Association Database entries on VPP node.
433 :param node: VPP node to add SAD entry on.
434 :param n_entries: Number of SAD entries to be created.
435 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
437 :param spi: Security Parameter Index of first SAD entry. All subsequent
438 SAD entries will have spi incremented by 1.
439 :param crypto_alg: The encryption algorithm name.
440 :param crypto_key: The encryption key string.
441 :param integ_alg: The integrity algorithm name.
442 :param integ_key: The integrity key string.
443 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
444 specified ESP transport mode is used.
445 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
446 not specified ESP transport mode is used.
451 :type crypto_alg: CryptoAlg
452 :type crypto_key: str
453 :type integ_alg: IntegAlg
455 :type tunnel_src: str
456 :type tunnel_dst: str
458 if isinstance(crypto_key, str):
459 crypto_key = crypto_key.encode(encoding=u"utf-8")
460 if isinstance(integ_key, str):
461 integ_key = integ_key.encode(encoding=u"utf-8")
462 if tunnel_src and tunnel_dst:
463 src_addr = ip_address(tunnel_src)
464 dst_addr = ip_address(tunnel_dst)
469 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
472 if int(n_entries) > 10:
473 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
475 with open(tmp_filename, 'w') as tmp_file:
476 for i in range(n_entries):
477 integ = f"integ-alg {integ_alg.alg_name} " \
478 f"integ-key {integ_key.hex()}" \
479 if integ_alg else u""
480 tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
481 f"tunnel-dst {dst_addr + i * addr_incr}" \
482 if tunnel_src and tunnel_dst else u""
483 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
484 f"crypto-alg {crypto_alg.alg_name} " \
485 f"crypto-key {crypto_key.hex()} " \
486 f"{integ} {tunnel}\n"
490 tmp_filename, node, timeout=300, json_out=False,
493 os.remove(tmp_filename)
497 length=len(crypto_key),
501 length=len(integ_key),
502 data=integ_key if integ_key else 0
505 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
506 if tunnel_src and tunnel_dst:
507 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
508 if src_addr.version == 6:
510 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
513 cmd = u"ipsec_sad_entry_add_del_v2"
514 err_msg = f"Failed to add Security Association Database entry " \
515 f"on host {node[u'host']}"
520 crypto_algorithm=crypto_alg.alg_int_repr,
522 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
525 tunnel_src=str(src_addr),
526 tunnel_dst=str(dst_addr),
528 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
530 dscp=int(IpDscp.IP_API_DSCP_CS0),
531 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
532 udp_src_port=4500, # default value in api
533 udp_dst_port=4500 # default value in api
539 with PapiSocketExecutor(node) as papi_exec:
540 for i in range(n_entries):
541 args[u"entry"][u"sad_id"] = int(sad_id) + i
542 args[u"entry"][u"spi"] = int(spi) + i
543 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
544 if tunnel_src and tunnel_dst else src_addr
545 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
546 if tunnel_src and tunnel_dst else dst_addr
547 history = bool(not 1 < i < n_entries - 2)
548 papi_exec.add(cmd, history=history, **args)
549 papi_exec.get_replies(err_msg)
552 def vpp_ipsec_set_ip_route(
553 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
555 """Set IP address and route on interface.
557 :param node: VPP node to add config on.
558 :param n_tunnels: Number of tunnels to create.
559 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
560 :param traffic_addr: Traffic destination IP address to route.
561 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
562 :param interface: Interface key on node 1.
563 :param raddr_range: Mask specifying range of Policy selector Remote IP
564 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
568 :type tunnel_src: str
569 :type traffic_addr: str
570 :type tunnel_dst: str
572 :type raddr_range: int
574 tunnel_src = ip_address(tunnel_src)
575 tunnel_dst = ip_address(tunnel_dst)
576 traffic_addr = ip_address(traffic_addr)
577 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
578 else 1 << (32 - raddr_range)
580 if int(n_tunnels) > 10:
581 tmp_filename = u"/tmp/ipsec_set_ip.script"
583 with open(tmp_filename, 'w') as tmp_file:
584 if_name = Topology.get_interface_name(node, interface)
585 for i in range(n_tunnels):
586 conf = f"exec set interface ip address {if_name} " \
587 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
588 f"exec ip route add {traffic_addr + i}/" \
589 f"{128 if traffic_addr.version == 6 else 32} " \
590 f"via {tunnel_dst + i * addr_incr} {if_name}\n"
592 VatExecutor().execute_script(
593 tmp_filename, node, timeout=300, json_out=False,
596 os.remove(tmp_filename)
599 cmd1 = u"sw_interface_add_del_address"
601 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
606 cmd2 = u"ip_route_add_del"
612 err_msg = f"Failed to configure IP addresses and IP routes " \
613 f"on interface {interface} on host {node[u'host']}"
615 with PapiSocketExecutor(node) as papi_exec:
616 for i in range(n_tunnels):
617 args1[u"prefix"] = IPUtil.create_prefix_object(
618 tunnel_src + i * addr_incr, raddr_range
620 args2[u"route"] = IPUtil.compose_vpp_route_structure(
621 node, traffic_addr + i,
622 prefix_len=128 if traffic_addr.version == 6 else 32,
623 interface=interface, gateway=tunnel_dst + i * addr_incr
625 history = bool(not 1 < i < n_tunnels - 2)
626 papi_exec.add(cmd1, history=history, **args1).\
627 add(cmd2, history=history, **args2)
628 papi_exec.get_replies(err_msg)
631 def vpp_ipsec_add_spd(node, spd_id):
632 """Create Security Policy Database on the VPP node.
634 :param node: VPP node to add SPD on.
635 :param spd_id: SPD ID.
639 cmd = u"ipsec_spd_add_del"
640 err_msg = f"Failed to add Security Policy Database " \
641 f"on host {node[u'host']}"
646 with PapiSocketExecutor(node) as papi_exec:
647 papi_exec.add(cmd, **args).get_reply(err_msg)
650 def vpp_ipsec_spd_add_if(node, spd_id, interface):
651 """Add interface to the Security Policy Database.
653 :param node: VPP node.
654 :param spd_id: SPD ID to add interface on.
655 :param interface: Interface name or sw_if_index.
658 :type interface: str or int
660 cmd = u"ipsec_interface_add_del_spd"
661 err_msg = f"Failed to add interface {interface} to Security Policy " \
662 f"Database {spd_id} on host {node[u'host']}"
665 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
668 with PapiSocketExecutor(node) as papi_exec:
669 papi_exec.add(cmd, **args).get_reply(err_msg)
672 def vpp_ipsec_policy_add(
673 node, spd_id, priority, action, inbound=True, sa_id=None,
674 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
675 rport_range=None, is_ipv6=False):
676 """Create Security Policy Database entry on the VPP node.
678 :param node: VPP node to add SPD entry on.
679 :param spd_id: SPD ID to add entry on.
680 :param priority: SPD entry priority, higher number = higher priority.
681 :param action: Policy action.
682 :param inbound: If True policy is for inbound traffic, otherwise
684 :param sa_id: SAD entry ID for protect action.
685 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
686 format IP/prefix or IP/mask. If no mask is provided,
687 it's considered to be /32.
688 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
689 format IP/prefix or IP/mask. If no mask is provided,
690 it's considered to be /32.
691 :param proto: Policy selector next layer protocol number.
692 :param lport_range: Policy selector local TCP/UDP port range in format
693 <port_start>-<port_end>.
694 :param rport_range: Policy selector remote TCP/UDP port range in format
695 <port_start>-<port_end>.
696 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
697 not defined so it will default to address ::/0, otherwise False.
701 :type action: PolicyAction
704 :type laddr_range: string
705 :type raddr_range: string
707 :type lport_range: string
708 :type rport_range: string
711 if laddr_range is None:
712 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
714 if raddr_range is None:
715 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
717 cmd = u"ipsec_spd_entry_add_del"
718 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
719 f"on host {node[u'host']}"
723 priority=int(priority),
724 is_outbound=not inbound,
725 sa_id=int(sa_id) if sa_id else 0,
726 policy=action.policy_int_repr,
727 protocol=int(proto) if proto else 0,
728 remote_address_start=IPAddress.create_ip_address_object(
729 ip_network(raddr_range, strict=False).network_address
731 remote_address_stop=IPAddress.create_ip_address_object(
732 ip_network(raddr_range, strict=False).broadcast_address
734 local_address_start=IPAddress.create_ip_address_object(
735 ip_network(laddr_range, strict=False).network_address
737 local_address_stop=IPAddress.create_ip_address_object(
738 ip_network(laddr_range, strict=False).broadcast_address
740 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
742 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
744 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
746 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
753 with PapiSocketExecutor(node) as papi_exec:
754 papi_exec.add(cmd, **args).get_reply(err_msg)
757 def vpp_ipsec_spd_add_entries(
758 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
760 """Create multiple Security Policy Database entries on the VPP node.
762 :param node: VPP node to add SPD entries on.
763 :param n_entries: Number of SPD entries to be added.
764 :param spd_id: SPD ID to add entries on.
765 :param priority: SPD entries priority, higher number = higher priority.
766 :param inbound: If True policy is for inbound traffic, otherwise
768 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
769 SAD entry ID incremented by 1.
770 :param raddr_ip: Policy selector remote IPv4 start address for the first
771 entry. Remote IPv4 end address will be calculated depending on
772 raddr_range parameter. Each subsequent entry will have start address
773 next after IPv4 end address of previous entry.
774 :param raddr_range: Required IP addres range.
782 :type raddr_range: int
784 raddr_ip = ip_address(raddr_ip)
785 if int(n_entries) > 10:
786 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
788 with open(tmp_filename, 'w') as tmp_file:
789 for i in range(n_entries):
790 direction = u'inbound' if inbound else u'outbound'
791 tunnel = f"exec ipsec policy add spd {spd_id} " \
792 f"priority {priority} {direction} " \
793 f"action protect sa {sa_id+i} " \
794 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
795 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
796 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
797 tmp_file.write(tunnel)
798 VatExecutor().execute_script(
799 tmp_filename, node, timeout=300, json_out=False,
802 os.remove(tmp_filename)
805 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
807 cmd = u"ipsec_spd_entry_add_del"
808 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
809 f"on host {node[u'host']}"
813 priority=int(priority),
814 is_outbound=not inbound,
815 sa_id=int(sa_id) if sa_id else 0,
816 policy=getattr(PolicyAction.PROTECT, u"policy_int_repr"),
818 remote_address_start=IPAddress.create_ip_address_object(raddr_ip),
819 remote_address_stop=IPAddress.create_ip_address_object(raddr_ip),
820 local_address_start=IPAddress.create_ip_address_object(
821 ip_network(laddr_range, strict=False).network_address
823 local_address_stop=IPAddress.create_ip_address_object(
824 ip_network(laddr_range, strict=False).broadcast_address
827 remote_port_stop=65535,
829 local_port_stop=65535
836 with PapiSocketExecutor(node) as papi_exec:
837 for i in range(n_entries):
838 args[u"entry"][u"remote_address_start"][u"un"] = \
839 IPAddress.union_addr(raddr_ip + i)
840 args[u"entry"][u"remote_address_stop"][u"un"] = \
841 IPAddress.union_addr(raddr_ip + i)
842 history = bool(not 1 < i < n_entries - 2)
843 papi_exec.add(cmd, history=history, **args)
844 papi_exec.get_replies(err_msg)
847 def _ipsec_create_tunnel_interfaces_dut1_vat(
848 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
849 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
850 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
852 :param nodes: VPP nodes to create tunnel interfaces.
853 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
854 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
855 IPv4/IPv6 address (ip2).
856 :param if1_key: VPP node 1 interface key from topology file.
857 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
858 interface key from topology file.
859 :param n_tunnels: Number of tunnel interfaces to be there at the end.
860 :param crypto_alg: The encryption algorithm name.
861 :param integ_alg: The integrity algorithm name.
862 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
863 first tunnel in direction node2->node1.
864 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
865 :param addr_incr: IP / IPv6 address incremental step.
866 :param existing_tunnels: Number of tunnel interfaces before creation.
867 Useful mainly for reconf tests. Default 0.
873 :type crypto_alg: CryptoAlg
874 :type integ_alg: IntegAlg
875 :type raddr_ip2: IPv4Address or IPv6Address
878 :type existing_tunnels: int
880 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
881 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
883 ckeys = [bytes()] * existing_tunnels
884 ikeys = [bytes()] * existing_tunnels
887 with open(tmp_fn1, u"w") as tmp_f1:
888 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
889 if u"DUT2" in nodes.keys() \
890 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
891 if not existing_tunnels:
893 f"exec create loopback interface\n"
894 f"exec set interface state loop0 up\n"
895 f"exec set interface ip address {if1_n} "
896 f"{tun_ips[u'ip2'] - 1}/"
897 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
898 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
901 for i in range(existing_tunnels, n_tunnels):
903 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
907 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
909 integ = f"integ-alg {integ_alg.alg_name} " \
910 f"integ-key {ikeys[i].hex()} "
914 f"exec set interface ip address loop0 "
915 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
916 f"exec create ipip tunnel "
917 f"src {tun_ips[u'ip1'] + i * addr_incr} "
918 f"dst {tun_ips[u'ip2']} "
920 f"exec ipsec sa add {i} "
921 f"spi {spi_d[u'spi_1'] + i} "
922 f"crypto-alg {crypto_alg.alg_name} "
923 f"crypto-key {ckeys[i].hex()} "
926 f"exec ipsec sa add {100000 + i} "
927 f"spi {spi_d[u'spi_2'] + i} "
928 f"crypto-alg {crypto_alg.alg_name} "
929 f"crypto-key {ckeys[i].hex()} "
932 f"exec ipsec tunnel protect ipip{i} "
934 f"sa-in {100000 + i} "
938 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
939 copy_on_execute=True,
940 history=bool(n_tunnels < 100)
944 with open(tmp_fn1, 'w') as tmp_f1:
945 for i in range(existing_tunnels, n_tunnels):
947 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
948 f"exec set interface state ipip{i} up\n"
949 f"exec ip route add "
950 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
954 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
955 copy_on_execute=True,
956 history=bool(n_tunnels < 100)
963 def _ipsec_create_tunnel_interfaces_dut2_vat(
964 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
965 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
966 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
968 :param nodes: VPP nodes to create tunnel interfaces.
969 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
970 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
971 IPv4/IPv6 address (ip2).
972 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
973 interface key from topology file.
974 :param n_tunnels: Number of tunnel interfaces to be there at the end.
975 :param crypto_alg: The encryption algorithm name.
976 :param ckeys: List of encryption keys.
977 :param integ_alg: The integrity algorithm name.
978 :param ikeys: List of integrity keys.
979 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
980 :param addr_incr: IP / IPv6 address incremental step.
981 :param existing_tunnels: Number of tunnel interfaces before creation.
982 Useful mainly for reconf tests. Default 0.
987 :type crypto_alg: CryptoAlg
989 :type integ_alg: IntegAlg
993 :type existing_tunnels: int
995 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
996 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
999 with open(tmp_fn2, 'w') as tmp_f2:
1000 if not existing_tunnels:
1002 f"exec set interface ip address {if2_n}"
1003 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1005 for i in range(existing_tunnels, n_tunnels):
1007 integ = f"integ-alg {integ_alg.alg_name} " \
1008 f"integ-key {ikeys[i].hex()} "
1012 f"exec create ipip tunnel "
1013 f"src {tun_ips[u'ip2']} "
1014 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1016 f"exec ipsec sa add {100000 + i} "
1017 f"spi {spi_d[u'spi_2'] + i} "
1018 f"crypto-alg {crypto_alg.alg_name} "
1019 f"crypto-key {ckeys[i].hex()} "
1022 f"exec ipsec sa add {i} "
1023 f"spi {spi_d[u'spi_1'] + i} "
1024 f"crypto-alg {crypto_alg.alg_name} "
1025 f"crypto-key {ckeys[i].hex()} "
1028 f"exec ipsec tunnel protect ipip{i} "
1029 f"sa-out {100000 + i} "
1034 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1035 copy_on_execute=True,
1036 history=bool(n_tunnels < 100)
1040 with open(tmp_fn2, 'w') as tmp_f2:
1041 if not existing_tunnels:
1043 f"exec ip route add {tun_ips[u'ip1']}/8 "
1044 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1046 for i in range(existing_tunnels, n_tunnels):
1048 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1049 f"exec set interface state ipip{i} up\n"
1050 f"exec ip route add "
1051 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1055 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1056 copy_on_execute=True,
1057 history=bool(n_tunnels < 100)
1062 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1063 """Create loopback interface and set IP address on VPP node 1 interface
1066 :param nodes: VPP nodes to create tunnel interfaces.
1067 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1068 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1069 IPv4/IPv6 address (ip2).
1070 :param if1_key: VPP node 1 interface key from topology file.
1071 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1072 interface key from topology file.
1078 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1079 # Create loopback interface on DUT1, set it to up state
1080 cmd = u"create_loopback_instance"
1086 err_msg = f"Failed to create loopback interface " \
1087 f"on host {nodes[u'DUT1'][u'host']}"
1088 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1089 get_sw_if_index(err_msg)
1090 cmd = u"sw_interface_set_flags"
1092 sw_if_index=loop_sw_if_idx,
1093 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1095 err_msg = f"Failed to set loopback interface state up " \
1096 f"on host {nodes[u'DUT1'][u'host']}"
1097 papi_exec.add(cmd, **args).get_reply(err_msg)
1098 # Set IP address on VPP node 1 interface
1099 cmd = u"sw_interface_add_del_address"
1101 sw_if_index=InterfaceUtil.get_interface_index(
1102 nodes[u"DUT1"], if1_key
1106 prefix=IPUtil.create_prefix_object(
1107 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1111 err_msg = f"Failed to set IP address on interface {if1_key} " \
1112 f"on host {nodes[u'DUT1'][u'host']}"
1113 papi_exec.add(cmd, **args).get_reply(err_msg)
1114 cmd2 = u"ip_neighbor_add_del"
1118 sw_if_index=Topology.get_interface_sw_index(
1119 nodes[u"DUT1"], if1_key
1123 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1124 if u"DUT2" in nodes.keys()
1125 else Topology.get_interface_mac(
1126 nodes[u"TG"], if2_key
1129 ip_address=tun_ips[u"ip2"].compressed
1132 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1133 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1135 return loop_sw_if_idx
1138 def _ipsec_create_tunnel_interfaces_dut1_papi(
1139 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1140 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1141 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1143 :param nodes: VPP nodes to create tunnel interfaces.
1144 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1145 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1146 IPv4/IPv6 address (ip2).
1147 :param if1_key: VPP node 1 interface key from topology file.
1148 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1149 interface key from topology file.
1150 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1151 :param crypto_alg: The encryption algorithm name.
1152 :param integ_alg: The integrity algorithm name.
1153 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1154 first tunnel in direction node2->node1.
1155 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1156 :param addr_incr: IP / IPv6 address incremental step.
1157 :param existing_tunnels: Number of tunnel interfaces before creation.
1158 Useful mainly for reconf tests. Default 0.
1163 :type n_tunnels: int
1164 :type crypto_alg: CryptoAlg
1165 :type integ_alg: IntegAlg
1166 :type raddr_ip2: IPv4Address or IPv6Address
1167 :type addr_incr: int
1169 :type existing_tunnels: int
1171 if not existing_tunnels:
1172 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1173 nodes, tun_ips, if1_key, if2_key
1176 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1177 nodes[u"DUT1"], u"loop0"
1179 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1180 # Configure IP addresses on loop0 interface
1181 cmd = u"sw_interface_add_del_address"
1183 sw_if_index=loop_sw_if_idx,
1188 for i in range(existing_tunnels, n_tunnels):
1189 args[u"prefix"] = IPUtil.create_prefix_object(
1190 tun_ips[u"ip1"] + i * addr_incr,
1191 128 if tun_ips[u"ip1"].version == 6 else 32
1194 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1196 # Configure IPIP tunnel interfaces
1197 cmd = u"ipip_add_tunnel"
1199 instance=Constants.BITWISE_NON_ZERO,
1204 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1206 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1207 dscp=int(IpDscp.IP_API_DSCP_CS0)
1212 ipip_tunnels = [None] * existing_tunnels
1213 for i in range(existing_tunnels, n_tunnels):
1214 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1215 tun_ips[u"ip1"] + i * addr_incr
1217 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1221 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1223 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1224 f" {nodes[u'DUT1'][u'host']}"
1225 ipip_tunnels.extend(
1227 reply[u"sw_if_index"]
1228 for reply in papi_exec.get_replies(err_msg)
1229 if u"sw_if_index" in reply
1232 # Configure IPSec SAD entries
1233 ckeys = [bytes()] * existing_tunnels
1234 ikeys = [bytes()] * existing_tunnels
1235 cmd = u"ipsec_sad_entry_add_del_v2"
1247 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1248 crypto_algorithm=crypto_alg.alg_int_repr,
1250 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1251 integrity_key=i_key,
1256 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1258 dscp=int(IpDscp.IP_API_DSCP_CS0),
1261 udp_src_port=IPSEC_UDP_PORT_NONE,
1262 udp_dst_port=IPSEC_UDP_PORT_NONE
1268 for i in range(existing_tunnels, n_tunnels):
1270 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1274 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1276 # SAD entry for outband / tx path
1277 args[u"entry"][u"sad_id"] = i
1278 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1280 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1281 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1283 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1284 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1285 args[u"entry"][u"flags"] = int(
1286 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1289 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1291 # SAD entry for inband / rx path
1292 args[u"entry"][u"sad_id"] = 100000 + i
1293 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1295 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1296 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1298 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1299 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1300 args[u"entry"][u"flags"] = int(
1301 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1302 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1305 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1307 err_msg = f"Failed to add IPsec SAD entries on host" \
1308 f" {nodes[u'DUT1'][u'host']}"
1309 papi_exec.get_replies(err_msg)
1310 # Add protection for tunnels with IPSEC
1311 cmd = u"ipsec_tunnel_protect_update"
1314 via_label=MPLS_LABEL_INVALID,
1315 obj_id=Constants.BITWISE_NON_ZERO
1317 ipsec_tunnel_protect = dict(
1325 tunnel=ipsec_tunnel_protect
1327 for i in range(existing_tunnels, n_tunnels):
1328 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1329 args[u"tunnel"][u"sa_out"] = i
1330 args[u"tunnel"][u"sa_in"] = [100000 + i]
1332 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1334 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1335 f"on host {nodes[u'DUT1'][u'host']}"
1336 papi_exec.get_replies(err_msg)
1338 # Configure unnumbered interfaces
1339 cmd = u"sw_interface_set_unnumbered"
1342 sw_if_index=InterfaceUtil.get_interface_index(
1343 nodes[u"DUT1"], if1_key
1345 unnumbered_sw_if_index=0
1347 for i in range(existing_tunnels, n_tunnels):
1348 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1350 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1353 cmd = u"sw_interface_set_flags"
1356 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1358 for i in range(existing_tunnels, n_tunnels):
1359 args[u"sw_if_index"] = ipip_tunnels[i]
1361 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1363 # Configure IP routes
1364 cmd = u"ip_route_add_del"
1370 for i in range(existing_tunnels, n_tunnels):
1371 args[u"route"] = IPUtil.compose_vpp_route_structure(
1372 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1373 prefix_len=128 if raddr_ip2.version == 6 else 32,
1374 interface=ipip_tunnels[i]
1377 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1379 err_msg = f"Failed to add IP routes on host " \
1380 f"{nodes[u'DUT1'][u'host']}"
1381 papi_exec.get_replies(err_msg)
1386 def _ipsec_create_tunnel_interfaces_dut2_papi(
1387 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1388 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1389 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1391 :param nodes: VPP nodes to create tunnel interfaces.
1392 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1393 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1394 IPv4/IPv6 address (ip2).
1395 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1396 interface key from topology file.
1397 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1398 :param crypto_alg: The encryption algorithm name.
1399 :param ckeys: List of encryption keys.
1400 :param integ_alg: The integrity algorithm name.
1401 :param ikeys: List of integrity keys.
1402 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1403 :param addr_incr: IP / IPv6 address incremental step.
1404 :param existing_tunnels: Number of tunnel interfaces before creation.
1405 Useful mainly for reconf tests. Default 0.
1409 :type n_tunnels: int
1410 :type crypto_alg: CryptoAlg
1412 :type integ_alg: IntegAlg
1414 :type addr_incr: int
1416 :type existing_tunnels: int
1418 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1419 if not existing_tunnels:
1420 # Set IP address on VPP node 2 interface
1421 cmd = u"sw_interface_add_del_address"
1423 sw_if_index=InterfaceUtil.get_interface_index(
1424 nodes[u"DUT2"], if2_key
1428 prefix=IPUtil.create_prefix_object(
1429 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1433 err_msg = f"Failed to set IP address on interface {if2_key} " \
1434 f"on host {nodes[u'DUT2'][u'host']}"
1435 papi_exec.add(cmd, **args).get_reply(err_msg)
1436 # Configure IPIP tunnel interfaces
1437 cmd = u"ipip_add_tunnel"
1439 instance=Constants.BITWISE_NON_ZERO,
1444 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1446 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1447 dscp=int(IpDscp.IP_API_DSCP_CS0)
1452 ipip_tunnels = [None] * existing_tunnels
1453 for i in range(existing_tunnels, n_tunnels):
1454 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1457 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1458 tun_ips[u"ip1"] + i * addr_incr
1461 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1463 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1464 f" {nodes[u'DUT2'][u'host']}"
1465 ipip_tunnels.extend(
1467 reply[u"sw_if_index"]
1468 for reply in papi_exec.get_replies(err_msg)
1469 if u"sw_if_index" in reply
1472 # Configure IPSec SAD entries
1473 cmd = u"ipsec_sad_entry_add_del_v2"
1485 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1487 crypto_algorithm=crypto_alg.alg_int_repr,
1489 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1490 integrity_key=i_key,
1496 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1498 dscp=int(IpDscp.IP_API_DSCP_CS0),
1501 udp_src_port=IPSEC_UDP_PORT_NONE,
1502 udp_dst_port=IPSEC_UDP_PORT_NONE
1508 for i in range(existing_tunnels, n_tunnels):
1510 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1514 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1516 # SAD entry for outband / tx path
1517 args[u"entry"][u"sad_id"] = 100000 + i
1518 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1520 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1521 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1523 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1524 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1525 args[u"entry"][u"flags"] = int(
1526 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1529 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1531 # SAD entry for inband / rx path
1532 args[u"entry"][u"sad_id"] = i
1533 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1535 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1536 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1538 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1539 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1540 args[u"entry"][u"flags"] = int(
1541 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1542 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1545 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1547 err_msg = f"Failed to add IPsec SAD entries on host" \
1548 f" {nodes[u'DUT2'][u'host']}"
1549 papi_exec.get_replies(err_msg)
1550 # Add protection for tunnels with IPSEC
1551 cmd = u"ipsec_tunnel_protect_update"
1554 via_label=MPLS_LABEL_INVALID,
1555 obj_id=Constants.BITWISE_NON_ZERO
1557 ipsec_tunnel_protect = dict(
1565 tunnel=ipsec_tunnel_protect
1567 for i in range(existing_tunnels, n_tunnels):
1568 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1569 args[u"tunnel"][u"sa_out"] = 100000 + i
1570 args[u"tunnel"][u"sa_in"] = [i]
1572 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1574 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1575 f"on host {nodes[u'DUT2'][u'host']}"
1576 papi_exec.get_replies(err_msg)
1578 if not existing_tunnels:
1579 # Configure IP route
1580 cmd = u"ip_route_add_del"
1581 route = IPUtil.compose_vpp_route_structure(
1582 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1583 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1585 gateway=(tun_ips[u"ip2"] - 1).compressed
1592 papi_exec.add(cmd, **args)
1593 # Configure unnumbered interfaces
1594 cmd = u"sw_interface_set_unnumbered"
1597 sw_if_index=InterfaceUtil.get_interface_index(
1598 nodes[u"DUT2"], if2_key
1600 unnumbered_sw_if_index=0
1602 for i in range(existing_tunnels, n_tunnels):
1603 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1605 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1608 cmd = u"sw_interface_set_flags"
1611 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1613 for i in range(existing_tunnels, n_tunnels):
1614 args[u"sw_if_index"] = ipip_tunnels[i]
1616 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1618 # Configure IP routes
1619 cmd = u"ip_route_add_del"
1625 for i in range(existing_tunnels, n_tunnels):
1626 args[u"route"] = IPUtil.compose_vpp_route_structure(
1627 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1628 prefix_len=128 if raddr_ip1.version == 6 else 32,
1629 interface=ipip_tunnels[i]
1632 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1634 err_msg = f"Failed to add IP routes " \
1635 f"on host {nodes[u'DUT2'][u'host']}"
1636 papi_exec.get_replies(err_msg)
1639 def vpp_ipsec_create_tunnel_interfaces(
1640 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1641 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1642 existing_tunnels=0):
1643 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1645 :param nodes: VPP nodes to create tunnel interfaces.
1646 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1648 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1650 :param if1_key: VPP node 1 interface key from topology file.
1651 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1652 interface key from topology file.
1653 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1654 :param crypto_alg: The encryption algorithm name.
1655 :param integ_alg: The integrity algorithm name.
1656 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1657 first tunnel in direction node1->node2.
1658 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1659 first tunnel in direction node2->node1.
1660 :param raddr_range: Mask specifying range of Policy selector Remote
1661 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1662 and to 128 in case of IPv6.
1663 :param existing_tunnels: Number of tunnel interfaces before creation.
1664 Useful mainly for reconf tests. Default 0.
1666 :type tun_if1_ip_addr: str
1667 :type tun_if2_ip_addr: str
1670 :type n_tunnels: int
1671 :type crypto_alg: CryptoAlg
1672 :type integ_alg: IntegAlg
1673 :type raddr_ip1: string
1674 :type raddr_ip2: string
1675 :type raddr_range: int
1676 :type existing_tunnels: int
1678 n_tunnels = int(n_tunnels)
1679 existing_tunnels = int(existing_tunnels)
1685 ip1=ip_address(tun_if1_ip_addr),
1686 ip2=ip_address(tun_if2_ip_addr)
1688 raddr_ip1 = ip_address(raddr_ip1)
1689 raddr_ip2 = ip_address(raddr_ip2)
1690 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1691 else 1 << (32 - raddr_range)
1693 if n_tunnels - existing_tunnels > 10:
1694 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1695 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1696 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1698 if u"DUT2" not in nodes.keys():
1699 return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
1700 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1701 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1702 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
1705 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1706 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1707 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1709 if u"DUT2" not in nodes.keys():
1710 return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
1711 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1712 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1713 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
1716 return None, None, None, None
1719 def _create_ipsec_script_files(dut, instances):
1720 """Create script files for configuring IPsec in containers
1722 :param dut: DUT node on which to create the script files
1723 :param instances: number of containers on DUT node
1725 :type instances: int
1728 for cnf in range(0, instances):
1730 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1732 scripts.append(open(script_filename, 'w'))
1736 def _close_and_copy_ipsec_script_files(
1737 dut, nodes, instances, scripts):
1738 """Close created scripts and copy them to containers
1740 :param dut: DUT node on which to create the script files
1741 :param nodes: VPP nodes
1742 :param instances: number of containers on DUT node
1743 :param scripts: dictionary holding the script files
1746 :type instances: int
1749 for cnf in range(0, instances):
1750 scripts[cnf].close()
1752 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1754 scp_node(nodes[dut], script_filename, script_filename)
1758 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1759 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1760 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1761 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1763 :param nodes: VPP nodes to create tunnel interfaces.
1764 :param if1_ip_addr: VPP node 1 interface IP4 address.
1765 :param if2_ip_addr: VPP node 2 interface IP4 address.
1766 :param n_tunnels: Number of tunnell interfaces to create.
1767 :param crypto_alg: The encryption algorithm name.
1768 :param integ_alg: The integrity algorithm name.
1769 :param raddr_ip1: Policy selector remote IPv4 start address for the
1770 first tunnel in direction node1->node2.
1771 :param raddr_ip2: Policy selector remote IPv4 start address for the
1772 first tunnel in direction node2->node1.
1773 :param raddr_range: Mask specifying range of Policy selector Remote
1774 IPv4 addresses. Valid values are from 1 to 32.
1775 :param n_instances: Number of containers.
1777 :type if1_ip_addr: str
1778 :type if2_ip_addr: str
1779 :type n_tunnels: int
1780 :type crypto_alg: CryptoAlg
1781 :type integ_alg: IntegAlg
1782 :type raddr_ip1: string
1783 :type raddr_ip2: string
1784 :type raddr_range: int
1785 :type n_instances: int
1789 addr_incr = 1 << (32 - raddr_range)
1791 dut1_scripts = IPsecUtil._create_ipsec_script_files(
1792 u"DUT1", n_instances
1794 dut2_scripts = IPsecUtil._create_ipsec_script_files(
1795 u"DUT2", n_instances
1798 for cnf in range(0, n_instances):
1799 dut1_scripts[cnf].write(
1800 u"create loopback interface\n"
1801 u"set interface state loop0 up\n\n"
1803 dut2_scripts[cnf].write(
1804 f"ip route add {if1_ip_addr}/8 via "
1805 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1808 for tnl in range(0, n_tunnels):
1809 cnf = tnl % n_instances
1811 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1816 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1819 f"integ-alg {integ_alg.alg_name} "
1820 f"local-integ-key {ikey} "
1821 f"remote-integ-key {ikey} "
1823 # Configure tunnel end point(s) on left side
1824 dut1_scripts[cnf].write(
1825 u"set interface ip address loop0 "
1826 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1827 f"create ipsec tunnel "
1828 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1829 f"local-spi {spi_1 + tnl} "
1830 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1831 f"remote-spi {spi_2 + tnl} "
1832 f"crypto-alg {crypto_alg.alg_name} "
1833 f"local-crypto-key {ckey} "
1834 f"remote-crypto-key {ckey} "
1835 f"instance {tnl // n_instances} "
1838 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1839 f"set interface state ipip{tnl // n_instances} up\n"
1840 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1841 f"via ipip{tnl // n_instances}\n\n"
1843 # Configure tunnel end point(s) on right side
1844 dut2_scripts[cnf].write(
1845 f"set ip neighbor memif1/{cnf + 1} "
1846 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1847 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1848 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1849 f"local-spi {spi_2 + tnl} "
1850 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1851 f"remote-spi {spi_1 + tnl} "
1852 f"crypto-alg {crypto_alg.alg_name} "
1853 f"local-crypto-key {ckey} "
1854 f"remote-crypto-key {ckey} "
1855 f"instance {tnl // n_instances} "
1858 f"set interface unnumbered ipip{tnl // n_instances} "
1859 f"use memif1/{cnf + 1}\n"
1860 f"set interface state ipip{tnl // n_instances} up\n"
1861 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1862 f"via ipip{tnl // n_instances}\n\n"
1865 IPsecUtil._close_and_copy_ipsec_script_files(
1866 u"DUT1", nodes, n_instances, dut1_scripts)
1867 IPsecUtil._close_and_copy_ipsec_script_files(
1868 u"DUT2", nodes, n_instances, dut2_scripts)
1871 def vpp_ipsec_add_multiple_tunnels(
1872 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1873 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1874 """Create multiple IPsec tunnels between two VPP nodes.
1876 :param nodes: VPP nodes to create tunnels.
1877 :param interface1: Interface name or sw_if_index on node 1.
1878 :param interface2: Interface name or sw_if_index on node 2.
1879 :param n_tunnels: Number of tunnels to create.
1880 :param crypto_alg: The encryption algorithm name.
1881 :param integ_alg: The integrity algorithm name.
1882 :param tunnel_ip1: Tunnel node1 IPv4 address.
1883 :param tunnel_ip2: Tunnel node2 IPv4 address.
1884 :param raddr_ip1: Policy selector remote IPv4 start address for the
1885 first tunnel in direction node1->node2.
1886 :param raddr_ip2: Policy selector remote IPv4 start address for the
1887 first tunnel in direction node2->node1.
1888 :param raddr_range: Mask specifying range of Policy selector Remote
1889 IPv4 addresses. Valid values are from 1 to 32.
1891 :type interface1: str or int
1892 :type interface2: str or int
1893 :type n_tunnels: int
1894 :type crypto_alg: CryptoAlg
1895 :type integ_alg: IntegAlg
1896 :type tunnel_ip1: str
1897 :type tunnel_ip2: str
1898 :type raddr_ip1: string
1899 :type raddr_ip2: string
1900 :type raddr_range: int
1910 crypto_key = gen_key(
1911 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1913 integ_key = gen_key(
1914 IPsecUtil.get_integ_alg_key_len(integ_alg)
1915 ).decode() if integ_alg else u""
1917 IPsecUtil.vpp_ipsec_set_ip_route(
1918 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1919 interface1, raddr_range)
1920 IPsecUtil.vpp_ipsec_set_ip_route(
1921 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1922 interface2, raddr_range)
1924 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1925 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1926 IPsecUtil.vpp_ipsec_policy_add(
1927 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1928 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1930 IPsecUtil.vpp_ipsec_policy_add(
1931 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1932 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1935 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1936 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1937 IPsecUtil.vpp_ipsec_policy_add(
1938 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1939 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1941 IPsecUtil.vpp_ipsec_policy_add(
1942 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1943 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1946 IPsecUtil.vpp_ipsec_add_sad_entries(
1947 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1948 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1950 IPsecUtil.vpp_ipsec_spd_add_entries(
1951 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1954 IPsecUtil.vpp_ipsec_add_sad_entries(
1955 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1956 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1958 IPsecUtil.vpp_ipsec_spd_add_entries(
1959 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1962 IPsecUtil.vpp_ipsec_add_sad_entries(
1963 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1964 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1967 IPsecUtil.vpp_ipsec_spd_add_entries(
1968 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1971 IPsecUtil.vpp_ipsec_add_sad_entries(
1972 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1973 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1976 IPsecUtil.vpp_ipsec_spd_add_entries(
1977 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1981 def vpp_ipsec_show(node):
1982 """Run "show ipsec" debug CLI command.
1984 :param node: Node to run command on.
1987 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
1990 def show_ipsec_security_association(node):
1991 """Show IPSec security association.
1993 :param node: DUT node.
1999 PapiSocketExecutor.dump_and_log(node, cmds)