1 # Copyright (c) 2020 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_add_sad_entry(
325 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
326 integ_key=u"", tunnel_src=None, tunnel_dst=None):
327 """Create Security Association Database entry on the VPP node.
329 :param node: VPP node to add SAD entry on.
330 :param sad_id: SAD entry ID.
331 :param spi: Security Parameter Index of this SAD entry.
332 :param crypto_alg: The encryption algorithm name.
333 :param crypto_key: The encryption key string.
334 :param integ_alg: The integrity algorithm name.
335 :param integ_key: The integrity key string.
336 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
337 specified ESP transport mode is used.
338 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
339 not specified ESP transport mode is used.
343 :type crypto_alg: CryptoAlg
344 :type crypto_key: str
345 :type integ_alg: IntegAlg
347 :type tunnel_src: str
348 :type tunnel_dst: str
350 if isinstance(crypto_key, str):
351 crypto_key = crypto_key.encode(encoding=u"utf-8")
352 if isinstance(integ_key, str):
353 integ_key = integ_key.encode(encoding=u"utf-8")
355 length=len(crypto_key),
359 length=len(integ_key),
360 data=integ_key if integ_key else 0
363 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
364 if tunnel_src and tunnel_dst:
365 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
366 src_addr = ip_address(tunnel_src)
367 dst_addr = ip_address(tunnel_dst)
368 if src_addr.version == 6:
370 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
375 cmd = u"ipsec_sad_entry_add_del"
376 err_msg = f"Failed to add Security Association Database entry " \
377 f"on host {node[u'host']}"
381 crypto_algorithm=crypto_alg.alg_int_repr,
383 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
386 tunnel_src=str(src_addr),
387 tunnel_dst=str(dst_addr),
388 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
389 udp_src_port=4500, # default value in api
390 udp_dst_port=4500 # default value in api
396 with PapiSocketExecutor(node) as papi_exec:
397 papi_exec.add(cmd, **args).get_reply(err_msg)
400 def vpp_ipsec_add_sad_entries(
401 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
402 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
403 """Create multiple Security Association Database entries on VPP node.
405 :param node: VPP node to add SAD entry on.
406 :param n_entries: Number of SAD entries to be created.
407 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
409 :param spi: Security Parameter Index of first SAD entry. All subsequent
410 SAD entries will have spi incremented by 1.
411 :param crypto_alg: The encryption algorithm name.
412 :param crypto_key: The encryption key string.
413 :param integ_alg: The integrity algorithm name.
414 :param integ_key: The integrity key string.
415 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
416 specified ESP transport mode is used.
417 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
418 not specified ESP transport mode is used.
423 :type crypto_alg: CryptoAlg
424 :type crypto_key: str
425 :type integ_alg: IntegAlg
427 :type tunnel_src: str
428 :type tunnel_dst: str
430 if isinstance(crypto_key, str):
431 crypto_key = crypto_key.encode(encoding=u"utf-8")
432 if isinstance(integ_key, str):
433 integ_key = integ_key.encode(encoding=u"utf-8")
434 if tunnel_src and tunnel_dst:
435 src_addr = ip_address(tunnel_src)
436 dst_addr = ip_address(tunnel_dst)
441 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
444 if int(n_entries) > 10:
445 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
447 with open(tmp_filename, 'w') as tmp_file:
448 for i in range(n_entries):
449 integ = f"integ-alg {integ_alg.alg_name} " \
450 f"integ-key {integ_key.hex()}" \
451 if integ_alg else u""
452 tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
453 f"tunnel-dst {dst_addr + i * addr_incr}" \
454 if tunnel_src and tunnel_dst else u""
455 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
456 f"crypto-alg {crypto_alg.alg_name} " \
457 f"crypto-key {crypto_key.hex()} " \
458 f"{integ} {tunnel}\n"
462 tmp_filename, node, timeout=300, json_out=False,
465 os.remove(tmp_filename)
469 length=len(crypto_key),
473 length=len(integ_key),
474 data=integ_key if integ_key else 0
477 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
478 if tunnel_src and tunnel_dst:
479 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
480 if src_addr.version == 6:
482 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
485 cmd = u"ipsec_sad_entry_add_del"
486 err_msg = f"Failed to add Security Association Database entry " \
487 f"on host {node[u'host']}"
492 crypto_algorithm=crypto_alg.alg_int_repr,
494 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
497 tunnel_src=str(src_addr),
498 tunnel_dst=str(dst_addr),
499 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
500 udp_src_port=4500, # default value in api
501 udp_dst_port=4500 # default value in api
507 with PapiSocketExecutor(node) as papi_exec:
508 for i in range(n_entries):
509 args[u"entry"][u"sad_id"] = int(sad_id) + i
510 args[u"entry"][u"spi"] = int(spi) + i
511 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
512 if tunnel_src and tunnel_dst else src_addr
513 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
514 if tunnel_src and tunnel_dst else dst_addr
515 history = bool(not 1 < i < n_entries - 2)
516 papi_exec.add(cmd, history=history, **args)
517 papi_exec.get_replies(err_msg)
520 def vpp_ipsec_set_ip_route(
521 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
523 """Set IP address and route on interface.
525 :param node: VPP node to add config on.
526 :param n_tunnels: Number of tunnels to create.
527 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
528 :param traffic_addr: Traffic destination IP address to route.
529 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
530 :param interface: Interface key on node 1.
531 :param raddr_range: Mask specifying range of Policy selector Remote IP
532 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
536 :type tunnel_src: str
537 :type traffic_addr: str
538 :type tunnel_dst: str
540 :type raddr_range: int
542 tunnel_src = ip_address(tunnel_src)
543 tunnel_dst = ip_address(tunnel_dst)
544 traffic_addr = ip_address(traffic_addr)
545 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
546 else 1 << (32 - raddr_range)
548 if int(n_tunnels) > 10:
549 tmp_filename = u"/tmp/ipsec_set_ip.script"
551 with open(tmp_filename, 'w') as tmp_file:
552 if_name = Topology.get_interface_name(node, interface)
553 for i in range(n_tunnels):
554 conf = f"exec set interface ip address {if_name} " \
555 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
556 f"exec ip route add {traffic_addr + i}/" \
557 f"{128 if traffic_addr.version == 6 else 32} " \
558 f"via {tunnel_dst + i * addr_incr} {if_name}\n"
560 VatExecutor().execute_script(
561 tmp_filename, node, timeout=300, json_out=False,
564 os.remove(tmp_filename)
567 cmd1 = u"sw_interface_add_del_address"
569 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
574 cmd2 = u"ip_route_add_del"
580 err_msg = f"Failed to configure IP addresses and IP routes " \
581 f"on interface {interface} on host {node[u'host']}"
583 with PapiSocketExecutor(node) as papi_exec:
584 for i in range(n_tunnels):
585 args1[u"prefix"] = IPUtil.create_prefix_object(
586 tunnel_src + i * addr_incr, raddr_range
588 args2[u"route"] = IPUtil.compose_vpp_route_structure(
589 node, traffic_addr + i,
590 prefix_len=128 if traffic_addr.version == 6 else 32,
591 interface=interface, gateway=tunnel_dst + i * addr_incr
593 history = bool(not 1 < i < n_tunnels - 2)
594 papi_exec.add(cmd1, history=history, **args1).\
595 add(cmd2, history=history, **args2)
596 papi_exec.get_replies(err_msg)
599 def vpp_ipsec_add_spd(node, spd_id):
600 """Create Security Policy Database on the VPP node.
602 :param node: VPP node to add SPD on.
603 :param spd_id: SPD ID.
607 cmd = u"ipsec_spd_add_del"
608 err_msg = f"Failed to add Security Policy Database " \
609 f"on host {node[u'host']}"
614 with PapiSocketExecutor(node) as papi_exec:
615 papi_exec.add(cmd, **args).get_reply(err_msg)
618 def vpp_ipsec_spd_add_if(node, spd_id, interface):
619 """Add interface to the Security Policy Database.
621 :param node: VPP node.
622 :param spd_id: SPD ID to add interface on.
623 :param interface: Interface name or sw_if_index.
626 :type interface: str or int
628 cmd = u"ipsec_interface_add_del_spd"
629 err_msg = f"Failed to add interface {interface} to Security Policy " \
630 f"Database {spd_id} on host {node[u'host']}"
633 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
636 with PapiSocketExecutor(node) as papi_exec:
637 papi_exec.add(cmd, **args).get_reply(err_msg)
640 def vpp_ipsec_policy_add(
641 node, spd_id, priority, action, inbound=True, sa_id=None,
642 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
643 rport_range=None, is_ipv6=False):
644 """Create Security Policy Database entry on the VPP node.
646 :param node: VPP node to add SPD entry on.
647 :param spd_id: SPD ID to add entry on.
648 :param priority: SPD entry priority, higher number = higher priority.
649 :param action: Policy action.
650 :param inbound: If True policy is for inbound traffic, otherwise
652 :param sa_id: SAD entry ID for protect action.
653 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
654 format IP/prefix or IP/mask. If no mask is provided,
655 it's considered to be /32.
656 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
657 format IP/prefix or IP/mask. If no mask is provided,
658 it's considered to be /32.
659 :param proto: Policy selector next layer protocol number.
660 :param lport_range: Policy selector local TCP/UDP port range in format
661 <port_start>-<port_end>.
662 :param rport_range: Policy selector remote TCP/UDP port range in format
663 <port_start>-<port_end>.
664 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
665 not defined so it will default to address ::/0, otherwise False.
669 :type action: PolicyAction
672 :type laddr_range: string
673 :type raddr_range: string
675 :type lport_range: string
676 :type rport_range: string
679 if laddr_range is None:
680 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
682 if raddr_range is None:
683 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
685 cmd = u"ipsec_spd_entry_add_del"
686 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
687 f"on host {node[u'host']}"
691 priority=int(priority),
692 is_outbound=not inbound,
693 sa_id=int(sa_id) if sa_id else 0,
694 policy=action.policy_int_repr,
695 protocol=int(proto) if proto else 0,
696 remote_address_start=IPAddress.create_ip_address_object(
697 ip_network(raddr_range, strict=False).network_address
699 remote_address_stop=IPAddress.create_ip_address_object(
700 ip_network(raddr_range, strict=False).broadcast_address
702 local_address_start=IPAddress.create_ip_address_object(
703 ip_network(laddr_range, strict=False).network_address
705 local_address_stop=IPAddress.create_ip_address_object(
706 ip_network(laddr_range, strict=False).broadcast_address
708 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
710 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
712 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
714 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
721 with PapiSocketExecutor(node) as papi_exec:
722 papi_exec.add(cmd, **args).get_reply(err_msg)
725 def vpp_ipsec_spd_add_entries(
726 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
728 """Create multiple Security Policy Database entries on the VPP node.
730 :param node: VPP node to add SPD entries on.
731 :param n_entries: Number of SPD entries to be added.
732 :param spd_id: SPD ID to add entries on.
733 :param priority: SPD entries priority, higher number = higher priority.
734 :param inbound: If True policy is for inbound traffic, otherwise
736 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
737 SAD entry ID incremented by 1.
738 :param raddr_ip: Policy selector remote IPv4 start address for the first
739 entry. Remote IPv4 end address will be calculated depending on
740 raddr_range parameter. Each subsequent entry will have start address
741 next after IPv4 end address of previous entry.
742 :param raddr_range: Required IP addres range.
750 :type raddr_range: int
752 raddr_ip = ip_address(raddr_ip)
753 if int(n_entries) > 10:
754 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
756 with open(tmp_filename, 'w') as tmp_file:
757 for i in range(n_entries):
758 direction = u'inbound' if inbound else u'outbound'
759 tunnel = f"exec ipsec policy add spd {spd_id} " \
760 f"priority {priority} {direction} " \
761 f"action protect sa {sa_id+i} " \
762 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
763 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
764 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
765 tmp_file.write(tunnel)
766 VatExecutor().execute_script(
767 tmp_filename, node, timeout=300, json_out=False,
770 os.remove(tmp_filename)
773 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
775 cmd = u"ipsec_spd_entry_add_del"
776 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
777 f"on host {node[u'host']}"
781 priority=int(priority),
782 is_outbound=not inbound,
783 sa_id=int(sa_id) if sa_id else 0,
784 policy=getattr(PolicyAction.PROTECT, u"policy_int_repr"),
786 remote_address_start=IPAddress.create_ip_address_object(raddr_ip),
787 remote_address_stop=IPAddress.create_ip_address_object(raddr_ip),
788 local_address_start=IPAddress.create_ip_address_object(
789 ip_network(laddr_range, strict=False).network_address
791 local_address_stop=IPAddress.create_ip_address_object(
792 ip_network(laddr_range, strict=False).broadcast_address
795 remote_port_stop=65535,
797 local_port_stop=65535
804 with PapiSocketExecutor(node) as papi_exec:
805 for i in range(n_entries):
806 args[u"entry"][u"remote_address_start"][u"un"] = \
807 IPAddress.union_addr(raddr_ip + i)
808 args[u"entry"][u"remote_address_stop"][u"un"] = \
809 IPAddress.union_addr(raddr_ip + i)
810 history = bool(not 1 < i < n_entries - 2)
811 papi_exec.add(cmd, history=history, **args)
812 papi_exec.get_replies(err_msg)
815 def _ipsec_create_tunnel_interfaces_dut1_vat(
816 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
817 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
818 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
820 :param nodes: VPP nodes to create tunnel interfaces.
821 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
822 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
823 IPv4/IPv6 address (ip2).
824 :param if1_key: VPP node 1 interface key from topology file.
825 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
826 interface key from topology file.
827 :param n_tunnels: Number of tunnel interfaces to be there at the end.
828 :param crypto_alg: The encryption algorithm name.
829 :param integ_alg: The integrity algorithm name.
830 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
831 first tunnel in direction node2->node1.
832 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
833 :param addr_incr: IP / IPv6 address incremental step.
834 :param existing_tunnels: Number of tunnel interfaces before creation.
835 Useful mainly for reconf tests. Default 0.
841 :type crypto_alg: CryptoAlg
842 :type integ_alg: IntegAlg
843 :type raddr_ip2: IPv4Address or IPv6Address
846 :type existing_tunnels: int
848 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
849 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
851 ckeys = [bytes()] * existing_tunnels
852 ikeys = [bytes()] * existing_tunnels
855 with open(tmp_fn1, u"w") as tmp_f1:
856 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
857 if u"DUT2" in nodes.keys() \
858 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
859 if not existing_tunnels:
861 f"exec create loopback interface\n"
862 f"exec set interface state loop0 up\n"
863 f"exec set interface ip address {if1_n} "
864 f"{tun_ips[u'ip2'] - 1}/"
865 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
866 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
869 for i in range(existing_tunnels, n_tunnels):
871 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
875 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
877 integ = f"integ-alg {integ_alg.alg_name} " \
878 f"integ-key {ikeys[i].hex()} "
882 f"exec set interface ip address loop0 "
883 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
884 f"exec create ipip tunnel "
885 f"src {tun_ips[u'ip1'] + i * addr_incr} "
886 f"dst {tun_ips[u'ip2']} "
888 f"exec ipsec sa add {i} "
889 f"spi {spi_d[u'spi_1'] + i} "
890 f"crypto-alg {crypto_alg.alg_name} "
891 f"crypto-key {ckeys[i].hex()} "
894 f"exec ipsec sa add {100000 + i} "
895 f"spi {spi_d[u'spi_2'] + i} "
896 f"crypto-alg {crypto_alg.alg_name} "
897 f"crypto-key {ckeys[i].hex()} "
900 f"exec ipsec tunnel protect ipip{i} "
902 f"sa-in {100000 + i} "
906 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
907 copy_on_execute=True,
908 history=bool(n_tunnels < 100)
912 with open(tmp_fn1, 'w') as tmp_f1:
913 for i in range(existing_tunnels, n_tunnels):
915 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
916 f"exec set interface state ipip{i} up\n"
917 f"exec ip route add "
918 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
922 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
923 copy_on_execute=True,
924 history=bool(n_tunnels < 100)
931 def _ipsec_create_tunnel_interfaces_dut2_vat(
932 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
933 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
934 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
936 :param nodes: VPP nodes to create tunnel interfaces.
937 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
938 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
939 IPv4/IPv6 address (ip2).
940 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
941 interface key from topology file.
942 :param n_tunnels: Number of tunnel interfaces to be there at the end.
943 :param crypto_alg: The encryption algorithm name.
944 :param ckeys: List of encryption keys.
945 :param integ_alg: The integrity algorithm name.
946 :param ikeys: List of integrity keys.
947 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
948 :param addr_incr: IP / IPv6 address incremental step.
949 :param existing_tunnels: Number of tunnel interfaces before creation.
950 Useful mainly for reconf tests. Default 0.
955 :type crypto_alg: CryptoAlg
957 :type integ_alg: IntegAlg
961 :type existing_tunnels: int
963 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
964 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
967 with open(tmp_fn2, 'w') as tmp_f2:
968 if not existing_tunnels:
970 f"exec set interface ip address {if2_n}"
971 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
973 for i in range(existing_tunnels, n_tunnels):
975 integ = f"integ-alg {integ_alg.alg_name} " \
976 f"integ-key {ikeys[i].hex()} "
980 f"exec create ipip tunnel "
981 f"src {tun_ips[u'ip2']} "
982 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
984 f"exec ipsec sa add {100000 + i} "
985 f"spi {spi_d[u'spi_2'] + i} "
986 f"crypto-alg {crypto_alg.alg_name} "
987 f"crypto-key {ckeys[i].hex()} "
990 f"exec ipsec sa add {i} "
991 f"spi {spi_d[u'spi_1'] + i} "
992 f"crypto-alg {crypto_alg.alg_name} "
993 f"crypto-key {ckeys[i].hex()} "
996 f"exec ipsec tunnel protect ipip{i} "
997 f"sa-out {100000 + i} "
1002 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1003 copy_on_execute=True,
1004 history=bool(n_tunnels < 100)
1008 with open(tmp_fn2, 'w') as tmp_f2:
1009 if not existing_tunnels:
1011 f"exec ip route add {tun_ips[u'ip1']}/8 "
1012 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1014 for i in range(existing_tunnels, n_tunnels):
1016 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1017 f"exec set interface state ipip{i} up\n"
1018 f"exec ip route add "
1019 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1023 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1024 copy_on_execute=True,
1025 history=bool(n_tunnels < 100)
1030 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1031 """Create loopback interface and set IP address on VPP node 1 interface
1034 :param nodes: VPP nodes to create tunnel interfaces.
1035 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1036 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1037 IPv4/IPv6 address (ip2).
1038 :param if1_key: VPP node 1 interface key from topology file.
1039 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1040 interface key from topology file.
1046 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1047 # Create loopback interface on DUT1, set it to up state
1048 cmd = u"create_loopback"
1052 err_msg = f"Failed to create loopback interface " \
1053 f"on host {nodes[u'DUT1'][u'host']}"
1054 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1055 get_sw_if_index(err_msg)
1056 cmd = u"sw_interface_set_flags"
1058 sw_if_index=loop_sw_if_idx,
1059 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1061 err_msg = f"Failed to set loopback interface state up " \
1062 f"on host {nodes[u'DUT1'][u'host']}"
1063 papi_exec.add(cmd, **args).get_reply(err_msg)
1064 # Set IP address on VPP node 1 interface
1065 cmd = u"sw_interface_add_del_address"
1067 sw_if_index=InterfaceUtil.get_interface_index(
1068 nodes[u"DUT1"], if1_key
1072 prefix=IPUtil.create_prefix_object(
1073 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1077 err_msg = f"Failed to set IP address on interface {if1_key} " \
1078 f"on host {nodes[u'DUT1'][u'host']}"
1079 papi_exec.add(cmd, **args).get_reply(err_msg)
1080 cmd2 = u"ip_neighbor_add_del"
1084 sw_if_index=Topology.get_interface_sw_index(
1085 nodes[u"DUT1"], if1_key
1089 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1090 if u"DUT2" in nodes.keys()
1091 else Topology.get_interface_mac(
1092 nodes[u"TG"], if2_key
1095 ip_address=tun_ips[u"ip2"].compressed
1098 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1099 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1101 return loop_sw_if_idx
1104 def _ipsec_create_tunnel_interfaces_dut1_papi(
1105 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1106 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1107 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1109 :param nodes: VPP nodes to create tunnel interfaces.
1110 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1111 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1112 IPv4/IPv6 address (ip2).
1113 :param if1_key: VPP node 1 interface key from topology file.
1114 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1115 interface key from topology file.
1116 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1117 :param crypto_alg: The encryption algorithm name.
1118 :param integ_alg: The integrity algorithm name.
1119 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1120 first tunnel in direction node2->node1.
1121 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1122 :param addr_incr: IP / IPv6 address incremental step.
1123 :param existing_tunnels: Number of tunnel interfaces before creation.
1124 Useful mainly for reconf tests. Default 0.
1129 :type n_tunnels: int
1130 :type crypto_alg: CryptoAlg
1131 :type integ_alg: IntegAlg
1132 :type raddr_ip2: IPv4Address or IPv6Address
1133 :type addr_incr: int
1135 :type existing_tunnels: int
1137 if not existing_tunnels:
1138 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1139 nodes, tun_ips, if1_key, if2_key
1142 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1143 nodes[u"DUT1"], u"loop0"
1145 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1146 # Configure IP addresses on loop0 interface
1147 cmd = u"sw_interface_add_del_address"
1149 sw_if_index=loop_sw_if_idx,
1154 for i in range(existing_tunnels, n_tunnels):
1155 args[u"prefix"] = IPUtil.create_prefix_object(
1156 tun_ips[u"ip1"] + i * addr_incr,
1157 128 if tun_ips[u"ip1"].version == 6 else 32
1160 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1162 # Configure IPIP tunnel interfaces
1163 cmd = u"ipip_add_tunnel"
1165 instance=Constants.BITWISE_NON_ZERO,
1170 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1172 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1173 dscp=int(IpDscp.IP_API_DSCP_CS0)
1178 ipip_tunnels = [None] * existing_tunnels
1179 for i in range(existing_tunnels, n_tunnels):
1180 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1181 tun_ips[u"ip1"] + i * addr_incr
1183 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1187 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1189 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1190 f" {nodes[u'DUT1'][u'host']}"
1191 ipip_tunnels.extend(
1193 reply[u"sw_if_index"]
1194 for reply in papi_exec.get_replies(err_msg)
1195 if u"sw_if_index" in reply
1198 # Configure IPSec SAD entries
1199 ckeys = [bytes()] * existing_tunnels
1200 ikeys = [bytes()] * existing_tunnels
1201 cmd = u"ipsec_sad_entry_add_del_v2"
1213 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1214 crypto_algorithm=crypto_alg.alg_int_repr,
1216 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1217 integrity_key=i_key,
1222 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1224 dscp=int(IpDscp.IP_API_DSCP_CS0),
1227 udp_src_port=IPSEC_UDP_PORT_NONE,
1228 udp_dst_port=IPSEC_UDP_PORT_NONE
1234 for i in range(existing_tunnels, n_tunnels):
1236 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1240 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1242 # SAD entry for outband / tx path
1243 args[u"entry"][u"sad_id"] = i
1244 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1246 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1247 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1249 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1250 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1251 args[u"entry"][u"flags"] = int(
1252 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1255 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1257 # SAD entry for inband / rx path
1258 args[u"entry"][u"sad_id"] = 100000 + i
1259 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1261 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1262 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1264 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1265 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1266 args[u"entry"][u"flags"] = int(
1267 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1268 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1271 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1273 err_msg = f"Failed to add IPsec SAD entries on host" \
1274 f" {nodes[u'DUT1'][u'host']}"
1275 papi_exec.get_replies(err_msg)
1276 # Add protection for tunnels with IPSEC
1277 cmd = u"ipsec_tunnel_protect_update"
1280 via_label=MPLS_LABEL_INVALID,
1281 obj_id=Constants.BITWISE_NON_ZERO
1283 ipsec_tunnel_protect = dict(
1291 tunnel=ipsec_tunnel_protect
1293 for i in range(existing_tunnels, n_tunnels):
1294 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1295 args[u"tunnel"][u"sa_out"] = i
1296 args[u"tunnel"][u"sa_in"] = [100000 + i]
1298 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1300 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1301 f"on host {nodes[u'DUT1'][u'host']}"
1302 papi_exec.get_replies(err_msg)
1304 # Configure unnumbered interfaces
1305 cmd = u"sw_interface_set_unnumbered"
1308 sw_if_index=InterfaceUtil.get_interface_index(
1309 nodes[u"DUT1"], if1_key
1311 unnumbered_sw_if_index=0
1313 for i in range(existing_tunnels, n_tunnels):
1314 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1316 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1319 cmd = u"sw_interface_set_flags"
1322 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1324 for i in range(existing_tunnels, n_tunnels):
1325 args[u"sw_if_index"] = ipip_tunnels[i]
1327 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1329 # Configure IP routes
1330 cmd = u"ip_route_add_del"
1336 for i in range(existing_tunnels, n_tunnels):
1337 args[u"route"] = IPUtil.compose_vpp_route_structure(
1338 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1339 prefix_len=128 if raddr_ip2.version == 6 else 32,
1340 interface=ipip_tunnels[i]
1343 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1345 err_msg = f"Failed to add IP routes on host " \
1346 f"{nodes[u'DUT1'][u'host']}"
1347 papi_exec.get_replies(err_msg)
1352 def _ipsec_create_tunnel_interfaces_dut2_papi(
1353 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1354 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1355 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1357 :param nodes: VPP nodes to create tunnel interfaces.
1358 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1359 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1360 IPv4/IPv6 address (ip2).
1361 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1362 interface key from topology file.
1363 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1364 :param crypto_alg: The encryption algorithm name.
1365 :param ckeys: List of encryption keys.
1366 :param integ_alg: The integrity algorithm name.
1367 :param ikeys: List of integrity keys.
1368 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1369 :param addr_incr: IP / IPv6 address incremental step.
1370 :param existing_tunnels: Number of tunnel interfaces before creation.
1371 Useful mainly for reconf tests. Default 0.
1375 :type n_tunnels: int
1376 :type crypto_alg: CryptoAlg
1378 :type integ_alg: IntegAlg
1380 :type addr_incr: int
1382 :type existing_tunnels: int
1384 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1385 if not existing_tunnels:
1386 # Set IP address on VPP node 2 interface
1387 cmd = u"sw_interface_add_del_address"
1389 sw_if_index=InterfaceUtil.get_interface_index(
1390 nodes[u"DUT2"], if2_key
1394 prefix=IPUtil.create_prefix_object(
1395 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1399 err_msg = f"Failed to set IP address on interface {if2_key} " \
1400 f"on host {nodes[u'DUT2'][u'host']}"
1401 papi_exec.add(cmd, **args).get_reply(err_msg)
1402 # Configure IPIP tunnel interfaces
1403 cmd = u"ipip_add_tunnel"
1405 instance=Constants.BITWISE_NON_ZERO,
1410 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1412 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1413 dscp=int(IpDscp.IP_API_DSCP_CS0)
1418 ipip_tunnels = [None] * existing_tunnels
1419 for i in range(existing_tunnels, n_tunnels):
1420 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1423 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1424 tun_ips[u"ip1"] + i * addr_incr
1427 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1429 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1430 f" {nodes[u'DUT2'][u'host']}"
1431 ipip_tunnels.extend(
1433 reply[u"sw_if_index"]
1434 for reply in papi_exec.get_replies(err_msg)
1435 if u"sw_if_index" in reply
1438 # Configure IPSec SAD entries
1439 cmd = u"ipsec_sad_entry_add_del_v2"
1451 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1453 crypto_algorithm=crypto_alg.alg_int_repr,
1455 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1456 integrity_key=i_key,
1462 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1464 dscp=int(IpDscp.IP_API_DSCP_CS0),
1467 udp_src_port=IPSEC_UDP_PORT_NONE,
1468 udp_dst_port=IPSEC_UDP_PORT_NONE
1474 for i in range(existing_tunnels, n_tunnels):
1476 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1480 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1482 # SAD entry for outband / tx path
1483 args[u"entry"][u"sad_id"] = 100000 + i
1484 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1486 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1487 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1489 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1490 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1491 args[u"entry"][u"flags"] = int(
1492 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1495 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1497 # SAD entry for inband / rx path
1498 args[u"entry"][u"sad_id"] = i
1499 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1501 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1502 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1504 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1505 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1506 args[u"entry"][u"flags"] = int(
1507 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1508 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1511 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1513 err_msg = f"Failed to add IPsec SAD entries on host" \
1514 f" {nodes[u'DUT2'][u'host']}"
1515 papi_exec.get_replies(err_msg)
1516 # Add protection for tunnels with IPSEC
1517 cmd = u"ipsec_tunnel_protect_update"
1520 via_label=MPLS_LABEL_INVALID,
1521 obj_id=Constants.BITWISE_NON_ZERO
1523 ipsec_tunnel_protect = dict(
1531 tunnel=ipsec_tunnel_protect
1533 for i in range(existing_tunnels, n_tunnels):
1534 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1535 args[u"tunnel"][u"sa_out"] = 100000 + i
1536 args[u"tunnel"][u"sa_in"] = [i]
1538 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1540 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1541 f"on host {nodes[u'DUT2'][u'host']}"
1542 papi_exec.get_replies(err_msg)
1544 if not existing_tunnels:
1545 # Configure IP route
1546 cmd = u"ip_route_add_del"
1547 route = IPUtil.compose_vpp_route_structure(
1548 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1549 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1551 gateway=(tun_ips[u"ip2"] - 1).compressed
1558 papi_exec.add(cmd, **args)
1559 # Configure unnumbered interfaces
1560 cmd = u"sw_interface_set_unnumbered"
1563 sw_if_index=InterfaceUtil.get_interface_index(
1564 nodes[u"DUT2"], if2_key
1566 unnumbered_sw_if_index=0
1568 for i in range(existing_tunnels, n_tunnels):
1569 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1571 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1574 cmd = u"sw_interface_set_flags"
1577 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1579 for i in range(existing_tunnels, n_tunnels):
1580 args[u"sw_if_index"] = ipip_tunnels[i]
1582 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1584 # Configure IP routes
1585 cmd = u"ip_route_add_del"
1591 for i in range(existing_tunnels, n_tunnels):
1592 args[u"route"] = IPUtil.compose_vpp_route_structure(
1593 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1594 prefix_len=128 if raddr_ip1.version == 6 else 32,
1595 interface=ipip_tunnels[i]
1598 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1600 err_msg = f"Failed to add IP routes " \
1601 f"on host {nodes[u'DUT2'][u'host']}"
1602 papi_exec.get_replies(err_msg)
1605 def vpp_ipsec_create_tunnel_interfaces(
1606 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1607 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1608 existing_tunnels=0):
1609 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1611 :param nodes: VPP nodes to create tunnel interfaces.
1612 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1614 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1616 :param if1_key: VPP node 1 interface key from topology file.
1617 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1618 interface key from topology file.
1619 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1620 :param crypto_alg: The encryption algorithm name.
1621 :param integ_alg: The integrity algorithm name.
1622 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1623 first tunnel in direction node1->node2.
1624 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1625 first tunnel in direction node2->node1.
1626 :param raddr_range: Mask specifying range of Policy selector Remote
1627 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1628 and to 128 in case of IPv6.
1629 :param existing_tunnels: Number of tunnel interfaces before creation.
1630 Useful mainly for reconf tests. Default 0.
1632 :type tun_if1_ip_addr: str
1633 :type tun_if2_ip_addr: str
1636 :type n_tunnels: int
1637 :type crypto_alg: CryptoAlg
1638 :type integ_alg: IntegAlg
1639 :type raddr_ip1: string
1640 :type raddr_ip2: string
1641 :type raddr_range: int
1642 :type existing_tunnels: int
1644 n_tunnels = int(n_tunnels)
1645 existing_tunnels = int(existing_tunnels)
1651 ip1=ip_address(tun_if1_ip_addr),
1652 ip2=ip_address(tun_if2_ip_addr)
1654 raddr_ip1 = ip_address(raddr_ip1)
1655 raddr_ip2 = ip_address(raddr_ip2)
1656 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1657 else 1 << (32 - raddr_range)
1659 if n_tunnels - existing_tunnels > 10:
1660 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1661 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1662 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1664 if u"DUT2" not in nodes.keys():
1665 return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
1666 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1667 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1668 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
1671 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1672 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1673 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1675 if u"DUT2" not in nodes.keys():
1676 return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
1677 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1678 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1679 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
1682 return None, None, None, None
1685 def _create_ipsec_script_files(dut, instances):
1686 """Create script files for configuring IPsec in containers
1688 :param dut: DUT node on which to create the script files
1689 :param instances: number of containers on DUT node
1691 :type instances: int
1694 for cnf in range(0, instances):
1696 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1698 scripts.append(open(script_filename, 'w'))
1702 def _close_and_copy_ipsec_script_files(
1703 dut, nodes, instances, scripts):
1704 """Close created scripts and copy them to containers
1706 :param dut: DUT node on which to create the script files
1707 :param nodes: VPP nodes
1708 :param instances: number of containers on DUT node
1709 :param scripts: dictionary holding the script files
1712 :type instances: int
1715 for cnf in range(0, instances):
1716 scripts[cnf].close()
1718 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1720 scp_node(nodes[dut], script_filename, script_filename)
1724 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1725 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1726 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1727 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1729 :param nodes: VPP nodes to create tunnel interfaces.
1730 :param if1_ip_addr: VPP node 1 interface IP4 address.
1731 :param if2_ip_addr: VPP node 2 interface IP4 address.
1732 :param n_tunnels: Number of tunnell interfaces to create.
1733 :param crypto_alg: The encryption algorithm name.
1734 :param integ_alg: The integrity algorithm name.
1735 :param raddr_ip1: Policy selector remote IPv4 start address for the
1736 first tunnel in direction node1->node2.
1737 :param raddr_ip2: Policy selector remote IPv4 start address for the
1738 first tunnel in direction node2->node1.
1739 :param raddr_range: Mask specifying range of Policy selector Remote
1740 IPv4 addresses. Valid values are from 1 to 32.
1741 :param n_instances: Number of containers.
1743 :type if1_ip_addr: str
1744 :type if2_ip_addr: str
1745 :type n_tunnels: int
1746 :type crypto_alg: CryptoAlg
1747 :type integ_alg: IntegAlg
1748 :type raddr_ip1: string
1749 :type raddr_ip2: string
1750 :type raddr_range: int
1751 :type n_instances: int
1755 addr_incr = 1 << (32 - raddr_range)
1757 dut1_scripts = IPsecUtil._create_ipsec_script_files(
1758 u"DUT1", n_instances
1760 dut2_scripts = IPsecUtil._create_ipsec_script_files(
1761 u"DUT2", n_instances
1764 for cnf in range(0, n_instances):
1765 dut1_scripts[cnf].write(
1766 u"create loopback interface\n"
1767 u"set interface state loop0 up\n\n"
1769 dut2_scripts[cnf].write(
1770 f"ip route add {if1_ip_addr}/8 via "
1771 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1774 for tnl in range(0, n_tunnels):
1775 cnf = tnl % n_instances
1777 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1782 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1785 f"integ-alg {integ_alg.alg_name} "
1786 f"local-integ-key {ikey} "
1787 f"remote-integ-key {ikey} "
1789 # Configure tunnel end point(s) on left side
1790 dut1_scripts[cnf].write(
1791 u"set interface ip address loop0 "
1792 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1793 f"create ipsec tunnel "
1794 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1795 f"local-spi {spi_1 + tnl} "
1796 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1797 f"remote-spi {spi_2 + tnl} "
1798 f"crypto-alg {crypto_alg.alg_name} "
1799 f"local-crypto-key {ckey} "
1800 f"remote-crypto-key {ckey} "
1801 f"instance {tnl // n_instances} "
1804 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1805 f"set interface state ipip{tnl // n_instances} up\n"
1806 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1807 f"via ipip{tnl // n_instances}\n\n"
1809 # Configure tunnel end point(s) on right side
1810 dut2_scripts[cnf].write(
1811 f"set ip neighbor memif1/{cnf + 1} "
1812 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1813 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1814 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1815 f"local-spi {spi_2 + tnl} "
1816 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1817 f"remote-spi {spi_1 + tnl} "
1818 f"crypto-alg {crypto_alg.alg_name} "
1819 f"local-crypto-key {ckey} "
1820 f"remote-crypto-key {ckey} "
1821 f"instance {tnl // n_instances} "
1824 f"set interface unnumbered ipip{tnl // n_instances} "
1825 f"use memif1/{cnf + 1}\n"
1826 f"set interface state ipip{tnl // n_instances} up\n"
1827 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1828 f"via ipip{tnl // n_instances}\n\n"
1831 IPsecUtil._close_and_copy_ipsec_script_files(
1832 u"DUT1", nodes, n_instances, dut1_scripts)
1833 IPsecUtil._close_and_copy_ipsec_script_files(
1834 u"DUT2", nodes, n_instances, dut2_scripts)
1837 def vpp_ipsec_add_multiple_tunnels(
1838 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1839 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1840 """Create multiple IPsec tunnels between two VPP nodes.
1842 :param nodes: VPP nodes to create tunnels.
1843 :param interface1: Interface name or sw_if_index on node 1.
1844 :param interface2: Interface name or sw_if_index on node 2.
1845 :param n_tunnels: Number of tunnels to create.
1846 :param crypto_alg: The encryption algorithm name.
1847 :param integ_alg: The integrity algorithm name.
1848 :param tunnel_ip1: Tunnel node1 IPv4 address.
1849 :param tunnel_ip2: Tunnel node2 IPv4 address.
1850 :param raddr_ip1: Policy selector remote IPv4 start address for the
1851 first tunnel in direction node1->node2.
1852 :param raddr_ip2: Policy selector remote IPv4 start address for the
1853 first tunnel in direction node2->node1.
1854 :param raddr_range: Mask specifying range of Policy selector Remote
1855 IPv4 addresses. Valid values are from 1 to 32.
1857 :type interface1: str or int
1858 :type interface2: str or int
1859 :type n_tunnels: int
1860 :type crypto_alg: CryptoAlg
1861 :type integ_alg: IntegAlg
1862 :type tunnel_ip1: str
1863 :type tunnel_ip2: str
1864 :type raddr_ip1: string
1865 :type raddr_ip2: string
1866 :type raddr_range: int
1876 crypto_key = gen_key(
1877 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1879 integ_key = gen_key(
1880 IPsecUtil.get_integ_alg_key_len(integ_alg)
1881 ).decode() if integ_alg else u""
1883 IPsecUtil.vpp_ipsec_set_ip_route(
1884 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1885 interface1, raddr_range)
1886 IPsecUtil.vpp_ipsec_set_ip_route(
1887 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1888 interface2, raddr_range)
1890 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1891 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1892 IPsecUtil.vpp_ipsec_policy_add(
1893 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1894 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1896 IPsecUtil.vpp_ipsec_policy_add(
1897 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1898 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1901 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1902 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1903 IPsecUtil.vpp_ipsec_policy_add(
1904 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1905 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1907 IPsecUtil.vpp_ipsec_policy_add(
1908 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1909 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1912 IPsecUtil.vpp_ipsec_add_sad_entries(
1913 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1914 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1916 IPsecUtil.vpp_ipsec_spd_add_entries(
1917 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1920 IPsecUtil.vpp_ipsec_add_sad_entries(
1921 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1922 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1924 IPsecUtil.vpp_ipsec_spd_add_entries(
1925 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1928 IPsecUtil.vpp_ipsec_add_sad_entries(
1929 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1930 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1933 IPsecUtil.vpp_ipsec_spd_add_entries(
1934 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1937 IPsecUtil.vpp_ipsec_add_sad_entries(
1938 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1939 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1942 IPsecUtil.vpp_ipsec_spd_add_entries(
1943 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1947 def vpp_ipsec_show(node):
1948 """Run "show ipsec" debug CLI command.
1950 :param node: Node to run command on.
1953 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
1956 def show_ipsec_security_association(node):
1957 """Show IPSec security association.
1959 :param node: DUT node.
1965 PapiSocketExecutor.dump_and_log(node, cmds)