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_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_v2"
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),
389 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
391 dscp=int(IpDscp.IP_API_DSCP_CS0),
392 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
393 udp_src_port=4500, # default value in api
394 udp_dst_port=4500 # default value in api
400 with PapiSocketExecutor(node) as papi_exec:
401 papi_exec.add(cmd, **args).get_reply(err_msg)
404 def vpp_ipsec_add_sad_entries(
405 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
406 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
407 """Create multiple Security Association Database entries on VPP node.
409 :param node: VPP node to add SAD entry on.
410 :param n_entries: Number of SAD entries to be created.
411 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
413 :param spi: Security Parameter Index of first SAD entry. All subsequent
414 SAD entries will have spi incremented by 1.
415 :param crypto_alg: The encryption algorithm name.
416 :param crypto_key: The encryption key string.
417 :param integ_alg: The integrity algorithm name.
418 :param integ_key: The integrity key string.
419 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
420 specified ESP transport mode is used.
421 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
422 not specified ESP transport mode is used.
427 :type crypto_alg: CryptoAlg
428 :type crypto_key: str
429 :type integ_alg: IntegAlg
431 :type tunnel_src: str
432 :type tunnel_dst: str
434 if isinstance(crypto_key, str):
435 crypto_key = crypto_key.encode(encoding=u"utf-8")
436 if isinstance(integ_key, str):
437 integ_key = integ_key.encode(encoding=u"utf-8")
438 if tunnel_src and tunnel_dst:
439 src_addr = ip_address(tunnel_src)
440 dst_addr = ip_address(tunnel_dst)
445 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
448 if int(n_entries) > 10:
449 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
451 with open(tmp_filename, 'w') as tmp_file:
452 for i in range(n_entries):
453 integ = f"integ-alg {integ_alg.alg_name} " \
454 f"integ-key {integ_key.hex()}" \
455 if integ_alg else u""
456 tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
457 f"tunnel-dst {dst_addr + i * addr_incr}" \
458 if tunnel_src and tunnel_dst else u""
459 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
460 f"crypto-alg {crypto_alg.alg_name} " \
461 f"crypto-key {crypto_key.hex()} " \
462 f"{integ} {tunnel}\n"
466 tmp_filename, node, timeout=300, json_out=False,
469 os.remove(tmp_filename)
473 length=len(crypto_key),
477 length=len(integ_key),
478 data=integ_key if integ_key else 0
481 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
482 if tunnel_src and tunnel_dst:
483 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
484 if src_addr.version == 6:
486 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
489 cmd = u"ipsec_sad_entry_add_del_v2"
490 err_msg = f"Failed to add Security Association Database entry " \
491 f"on host {node[u'host']}"
496 crypto_algorithm=crypto_alg.alg_int_repr,
498 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
501 tunnel_src=str(src_addr),
502 tunnel_dst=str(dst_addr),
504 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
506 dscp=int(IpDscp.IP_API_DSCP_CS0),
507 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
508 udp_src_port=4500, # default value in api
509 udp_dst_port=4500 # default value in api
515 with PapiSocketExecutor(node) as papi_exec:
516 for i in range(n_entries):
517 args[u"entry"][u"sad_id"] = int(sad_id) + i
518 args[u"entry"][u"spi"] = int(spi) + i
519 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
520 if tunnel_src and tunnel_dst else src_addr
521 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
522 if tunnel_src and tunnel_dst else dst_addr
523 history = bool(not 1 < i < n_entries - 2)
524 papi_exec.add(cmd, history=history, **args)
525 papi_exec.get_replies(err_msg)
528 def vpp_ipsec_set_ip_route(
529 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
531 """Set IP address and route on interface.
533 :param node: VPP node to add config on.
534 :param n_tunnels: Number of tunnels to create.
535 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
536 :param traffic_addr: Traffic destination IP address to route.
537 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
538 :param interface: Interface key on node 1.
539 :param raddr_range: Mask specifying range of Policy selector Remote IP
540 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
544 :type tunnel_src: str
545 :type traffic_addr: str
546 :type tunnel_dst: str
548 :type raddr_range: int
550 tunnel_src = ip_address(tunnel_src)
551 tunnel_dst = ip_address(tunnel_dst)
552 traffic_addr = ip_address(traffic_addr)
553 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
554 else 1 << (32 - raddr_range)
556 if int(n_tunnels) > 10:
557 tmp_filename = u"/tmp/ipsec_set_ip.script"
559 with open(tmp_filename, 'w') as tmp_file:
560 if_name = Topology.get_interface_name(node, interface)
561 for i in range(n_tunnels):
562 conf = f"exec set interface ip address {if_name} " \
563 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
564 f"exec ip route add {traffic_addr + i}/" \
565 f"{128 if traffic_addr.version == 6 else 32} " \
566 f"via {tunnel_dst + i * addr_incr} {if_name}\n"
568 VatExecutor().execute_script(
569 tmp_filename, node, timeout=300, json_out=False,
572 os.remove(tmp_filename)
575 cmd1 = u"sw_interface_add_del_address"
577 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
582 cmd2 = u"ip_route_add_del"
588 err_msg = f"Failed to configure IP addresses and IP routes " \
589 f"on interface {interface} on host {node[u'host']}"
591 with PapiSocketExecutor(node) as papi_exec:
592 for i in range(n_tunnels):
593 args1[u"prefix"] = IPUtil.create_prefix_object(
594 tunnel_src + i * addr_incr, raddr_range
596 args2[u"route"] = IPUtil.compose_vpp_route_structure(
597 node, traffic_addr + i,
598 prefix_len=128 if traffic_addr.version == 6 else 32,
599 interface=interface, gateway=tunnel_dst + i * addr_incr
601 history = bool(not 1 < i < n_tunnels - 2)
602 papi_exec.add(cmd1, history=history, **args1).\
603 add(cmd2, history=history, **args2)
604 papi_exec.get_replies(err_msg)
607 def vpp_ipsec_add_spd(node, spd_id):
608 """Create Security Policy Database on the VPP node.
610 :param node: VPP node to add SPD on.
611 :param spd_id: SPD ID.
615 cmd = u"ipsec_spd_add_del"
616 err_msg = f"Failed to add Security Policy Database " \
617 f"on host {node[u'host']}"
622 with PapiSocketExecutor(node) as papi_exec:
623 papi_exec.add(cmd, **args).get_reply(err_msg)
626 def vpp_ipsec_spd_add_if(node, spd_id, interface):
627 """Add interface to the Security Policy Database.
629 :param node: VPP node.
630 :param spd_id: SPD ID to add interface on.
631 :param interface: Interface name or sw_if_index.
634 :type interface: str or int
636 cmd = u"ipsec_interface_add_del_spd"
637 err_msg = f"Failed to add interface {interface} to Security Policy " \
638 f"Database {spd_id} on host {node[u'host']}"
641 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
644 with PapiSocketExecutor(node) as papi_exec:
645 papi_exec.add(cmd, **args).get_reply(err_msg)
648 def vpp_ipsec_policy_add(
649 node, spd_id, priority, action, inbound=True, sa_id=None,
650 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
651 rport_range=None, is_ipv6=False):
652 """Create Security Policy Database entry on the VPP node.
654 :param node: VPP node to add SPD entry on.
655 :param spd_id: SPD ID to add entry on.
656 :param priority: SPD entry priority, higher number = higher priority.
657 :param action: Policy action.
658 :param inbound: If True policy is for inbound traffic, otherwise
660 :param sa_id: SAD entry ID for protect action.
661 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
662 format IP/prefix or IP/mask. If no mask is provided,
663 it's considered to be /32.
664 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
665 format IP/prefix or IP/mask. If no mask is provided,
666 it's considered to be /32.
667 :param proto: Policy selector next layer protocol number.
668 :param lport_range: Policy selector local TCP/UDP port range in format
669 <port_start>-<port_end>.
670 :param rport_range: Policy selector remote TCP/UDP port range in format
671 <port_start>-<port_end>.
672 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
673 not defined so it will default to address ::/0, otherwise False.
677 :type action: PolicyAction
680 :type laddr_range: string
681 :type raddr_range: string
683 :type lport_range: string
684 :type rport_range: string
687 if laddr_range is None:
688 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
690 if raddr_range is None:
691 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
693 cmd = u"ipsec_spd_entry_add_del"
694 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
695 f"on host {node[u'host']}"
699 priority=int(priority),
700 is_outbound=not inbound,
701 sa_id=int(sa_id) if sa_id else 0,
702 policy=action.policy_int_repr,
703 protocol=int(proto) if proto else 0,
704 remote_address_start=IPAddress.create_ip_address_object(
705 ip_network(raddr_range, strict=False).network_address
707 remote_address_stop=IPAddress.create_ip_address_object(
708 ip_network(raddr_range, strict=False).broadcast_address
710 local_address_start=IPAddress.create_ip_address_object(
711 ip_network(laddr_range, strict=False).network_address
713 local_address_stop=IPAddress.create_ip_address_object(
714 ip_network(laddr_range, strict=False).broadcast_address
716 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
718 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
720 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
722 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
729 with PapiSocketExecutor(node) as papi_exec:
730 papi_exec.add(cmd, **args).get_reply(err_msg)
733 def vpp_ipsec_spd_add_entries(
734 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
736 """Create multiple Security Policy Database entries on the VPP node.
738 :param node: VPP node to add SPD entries on.
739 :param n_entries: Number of SPD entries to be added.
740 :param spd_id: SPD ID to add entries on.
741 :param priority: SPD entries priority, higher number = higher priority.
742 :param inbound: If True policy is for inbound traffic, otherwise
744 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
745 SAD entry ID incremented by 1.
746 :param raddr_ip: Policy selector remote IPv4 start address for the first
747 entry. Remote IPv4 end address will be calculated depending on
748 raddr_range parameter. Each subsequent entry will have start address
749 next after IPv4 end address of previous entry.
750 :param raddr_range: Required IP addres range.
758 :type raddr_range: int
760 raddr_ip = ip_address(raddr_ip)
761 if int(n_entries) > 10:
762 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
764 with open(tmp_filename, 'w') as tmp_file:
765 for i in range(n_entries):
766 direction = u'inbound' if inbound else u'outbound'
767 tunnel = f"exec ipsec policy add spd {spd_id} " \
768 f"priority {priority} {direction} " \
769 f"action protect sa {sa_id+i} " \
770 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
771 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
772 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
773 tmp_file.write(tunnel)
774 VatExecutor().execute_script(
775 tmp_filename, node, timeout=300, json_out=False,
778 os.remove(tmp_filename)
781 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
783 cmd = u"ipsec_spd_entry_add_del"
784 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
785 f"on host {node[u'host']}"
789 priority=int(priority),
790 is_outbound=not inbound,
791 sa_id=int(sa_id) if sa_id else 0,
792 policy=getattr(PolicyAction.PROTECT, u"policy_int_repr"),
794 remote_address_start=IPAddress.create_ip_address_object(raddr_ip),
795 remote_address_stop=IPAddress.create_ip_address_object(raddr_ip),
796 local_address_start=IPAddress.create_ip_address_object(
797 ip_network(laddr_range, strict=False).network_address
799 local_address_stop=IPAddress.create_ip_address_object(
800 ip_network(laddr_range, strict=False).broadcast_address
803 remote_port_stop=65535,
805 local_port_stop=65535
812 with PapiSocketExecutor(node) as papi_exec:
813 for i in range(n_entries):
814 args[u"entry"][u"remote_address_start"][u"un"] = \
815 IPAddress.union_addr(raddr_ip + i)
816 args[u"entry"][u"remote_address_stop"][u"un"] = \
817 IPAddress.union_addr(raddr_ip + i)
818 history = bool(not 1 < i < n_entries - 2)
819 papi_exec.add(cmd, history=history, **args)
820 papi_exec.get_replies(err_msg)
823 def _ipsec_create_tunnel_interfaces_dut1_vat(
824 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
825 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
826 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
828 :param nodes: VPP nodes to create tunnel interfaces.
829 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
830 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
831 IPv4/IPv6 address (ip2).
832 :param if1_key: VPP node 1 interface key from topology file.
833 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
834 interface key from topology file.
835 :param n_tunnels: Number of tunnel interfaces to be there at the end.
836 :param crypto_alg: The encryption algorithm name.
837 :param integ_alg: The integrity algorithm name.
838 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
839 first tunnel in direction node2->node1.
840 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
841 :param addr_incr: IP / IPv6 address incremental step.
842 :param existing_tunnels: Number of tunnel interfaces before creation.
843 Useful mainly for reconf tests. Default 0.
849 :type crypto_alg: CryptoAlg
850 :type integ_alg: IntegAlg
851 :type raddr_ip2: IPv4Address or IPv6Address
854 :type existing_tunnels: int
856 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
857 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
859 ckeys = [bytes()] * existing_tunnels
860 ikeys = [bytes()] * existing_tunnels
863 with open(tmp_fn1, u"w") as tmp_f1:
864 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
865 if u"DUT2" in nodes.keys() \
866 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
867 if not existing_tunnels:
869 f"exec create loopback interface\n"
870 f"exec set interface state loop0 up\n"
871 f"exec set interface ip address {if1_n} "
872 f"{tun_ips[u'ip2'] - 1}/"
873 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
874 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
877 for i in range(existing_tunnels, n_tunnels):
879 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
883 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
885 integ = f"integ-alg {integ_alg.alg_name} " \
886 f"integ-key {ikeys[i].hex()} "
890 f"exec set interface ip address loop0 "
891 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
892 f"exec create ipip tunnel "
893 f"src {tun_ips[u'ip1'] + i * addr_incr} "
894 f"dst {tun_ips[u'ip2']} "
896 f"exec ipsec sa add {i} "
897 f"spi {spi_d[u'spi_1'] + i} "
898 f"crypto-alg {crypto_alg.alg_name} "
899 f"crypto-key {ckeys[i].hex()} "
902 f"exec ipsec sa add {100000 + i} "
903 f"spi {spi_d[u'spi_2'] + i} "
904 f"crypto-alg {crypto_alg.alg_name} "
905 f"crypto-key {ckeys[i].hex()} "
908 f"exec ipsec tunnel protect ipip{i} "
910 f"sa-in {100000 + i} "
914 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
915 copy_on_execute=True,
916 history=bool(n_tunnels < 100)
920 with open(tmp_fn1, 'w') as tmp_f1:
921 for i in range(existing_tunnels, n_tunnels):
923 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
924 f"exec set interface state ipip{i} up\n"
925 f"exec ip route add "
926 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
930 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
931 copy_on_execute=True,
932 history=bool(n_tunnels < 100)
939 def _ipsec_create_tunnel_interfaces_dut2_vat(
940 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
941 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
942 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
944 :param nodes: VPP nodes to create tunnel interfaces.
945 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
946 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
947 IPv4/IPv6 address (ip2).
948 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
949 interface key from topology file.
950 :param n_tunnels: Number of tunnel interfaces to be there at the end.
951 :param crypto_alg: The encryption algorithm name.
952 :param ckeys: List of encryption keys.
953 :param integ_alg: The integrity algorithm name.
954 :param ikeys: List of integrity keys.
955 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
956 :param addr_incr: IP / IPv6 address incremental step.
957 :param existing_tunnels: Number of tunnel interfaces before creation.
958 Useful mainly for reconf tests. Default 0.
963 :type crypto_alg: CryptoAlg
965 :type integ_alg: IntegAlg
969 :type existing_tunnels: int
971 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
972 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
975 with open(tmp_fn2, 'w') as tmp_f2:
976 if not existing_tunnels:
978 f"exec set interface ip address {if2_n}"
979 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
981 for i in range(existing_tunnels, n_tunnels):
983 integ = f"integ-alg {integ_alg.alg_name} " \
984 f"integ-key {ikeys[i].hex()} "
988 f"exec create ipip tunnel "
989 f"src {tun_ips[u'ip2']} "
990 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
992 f"exec ipsec sa add {100000 + i} "
993 f"spi {spi_d[u'spi_2'] + i} "
994 f"crypto-alg {crypto_alg.alg_name} "
995 f"crypto-key {ckeys[i].hex()} "
998 f"exec ipsec sa add {i} "
999 f"spi {spi_d[u'spi_1'] + i} "
1000 f"crypto-alg {crypto_alg.alg_name} "
1001 f"crypto-key {ckeys[i].hex()} "
1004 f"exec ipsec tunnel protect ipip{i} "
1005 f"sa-out {100000 + i} "
1010 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1011 copy_on_execute=True,
1012 history=bool(n_tunnels < 100)
1016 with open(tmp_fn2, 'w') as tmp_f2:
1017 if not existing_tunnels:
1019 f"exec ip route add {tun_ips[u'ip1']}/8 "
1020 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1022 for i in range(existing_tunnels, n_tunnels):
1024 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1025 f"exec set interface state ipip{i} up\n"
1026 f"exec ip route add "
1027 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1031 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1032 copy_on_execute=True,
1033 history=bool(n_tunnels < 100)
1038 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1039 """Create loopback interface and set IP address on VPP node 1 interface
1042 :param nodes: VPP nodes to create tunnel interfaces.
1043 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1044 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1045 IPv4/IPv6 address (ip2).
1046 :param if1_key: VPP node 1 interface key from topology file.
1047 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1048 interface key from topology file.
1054 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1055 # Create loopback interface on DUT1, set it to up state
1056 cmd = u"create_loopback_instance"
1062 err_msg = f"Failed to create loopback interface " \
1063 f"on host {nodes[u'DUT1'][u'host']}"
1064 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1065 get_sw_if_index(err_msg)
1066 cmd = u"sw_interface_set_flags"
1068 sw_if_index=loop_sw_if_idx,
1069 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1071 err_msg = f"Failed to set loopback interface state up " \
1072 f"on host {nodes[u'DUT1'][u'host']}"
1073 papi_exec.add(cmd, **args).get_reply(err_msg)
1074 # Set IP address on VPP node 1 interface
1075 cmd = u"sw_interface_add_del_address"
1077 sw_if_index=InterfaceUtil.get_interface_index(
1078 nodes[u"DUT1"], if1_key
1082 prefix=IPUtil.create_prefix_object(
1083 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1087 err_msg = f"Failed to set IP address on interface {if1_key} " \
1088 f"on host {nodes[u'DUT1'][u'host']}"
1089 papi_exec.add(cmd, **args).get_reply(err_msg)
1090 cmd2 = u"ip_neighbor_add_del"
1094 sw_if_index=Topology.get_interface_sw_index(
1095 nodes[u"DUT1"], if1_key
1099 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1100 if u"DUT2" in nodes.keys()
1101 else Topology.get_interface_mac(
1102 nodes[u"TG"], if2_key
1105 ip_address=tun_ips[u"ip2"].compressed
1108 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1109 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1111 return loop_sw_if_idx
1114 def _ipsec_create_tunnel_interfaces_dut1_papi(
1115 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1116 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1117 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1119 :param nodes: VPP nodes to create tunnel interfaces.
1120 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1121 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1122 IPv4/IPv6 address (ip2).
1123 :param if1_key: VPP node 1 interface key from topology file.
1124 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1125 interface key from topology file.
1126 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1127 :param crypto_alg: The encryption algorithm name.
1128 :param integ_alg: The integrity algorithm name.
1129 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1130 first tunnel in direction node2->node1.
1131 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1132 :param addr_incr: IP / IPv6 address incremental step.
1133 :param existing_tunnels: Number of tunnel interfaces before creation.
1134 Useful mainly for reconf tests. Default 0.
1139 :type n_tunnels: int
1140 :type crypto_alg: CryptoAlg
1141 :type integ_alg: IntegAlg
1142 :type raddr_ip2: IPv4Address or IPv6Address
1143 :type addr_incr: int
1145 :type existing_tunnels: int
1147 if not existing_tunnels:
1148 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1149 nodes, tun_ips, if1_key, if2_key
1152 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1153 nodes[u"DUT1"], u"loop0"
1155 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1156 # Configure IP addresses on loop0 interface
1157 cmd = u"sw_interface_add_del_address"
1159 sw_if_index=loop_sw_if_idx,
1164 for i in range(existing_tunnels, n_tunnels):
1165 args[u"prefix"] = IPUtil.create_prefix_object(
1166 tun_ips[u"ip1"] + i * addr_incr,
1167 128 if tun_ips[u"ip1"].version == 6 else 32
1170 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1172 # Configure IPIP tunnel interfaces
1173 cmd = u"ipip_add_tunnel"
1175 instance=Constants.BITWISE_NON_ZERO,
1180 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1182 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1183 dscp=int(IpDscp.IP_API_DSCP_CS0)
1188 ipip_tunnels = [None] * existing_tunnels
1189 for i in range(existing_tunnels, n_tunnels):
1190 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1191 tun_ips[u"ip1"] + i * addr_incr
1193 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1197 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1199 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1200 f" {nodes[u'DUT1'][u'host']}"
1201 ipip_tunnels.extend(
1203 reply[u"sw_if_index"]
1204 for reply in papi_exec.get_replies(err_msg)
1205 if u"sw_if_index" in reply
1208 # Configure IPSec SAD entries
1209 ckeys = [bytes()] * existing_tunnels
1210 ikeys = [bytes()] * existing_tunnels
1211 cmd = u"ipsec_sad_entry_add_del_v2"
1223 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1224 crypto_algorithm=crypto_alg.alg_int_repr,
1226 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1227 integrity_key=i_key,
1232 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1234 dscp=int(IpDscp.IP_API_DSCP_CS0),
1237 udp_src_port=IPSEC_UDP_PORT_NONE,
1238 udp_dst_port=IPSEC_UDP_PORT_NONE
1244 for i in range(existing_tunnels, n_tunnels):
1246 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1250 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1252 # SAD entry for outband / tx path
1253 args[u"entry"][u"sad_id"] = i
1254 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1256 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1257 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1259 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1260 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1261 args[u"entry"][u"flags"] = int(
1262 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1265 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1267 # SAD entry for inband / rx path
1268 args[u"entry"][u"sad_id"] = 100000 + i
1269 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1271 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1272 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1274 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1275 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1276 args[u"entry"][u"flags"] = int(
1277 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1278 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1281 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1283 err_msg = f"Failed to add IPsec SAD entries on host" \
1284 f" {nodes[u'DUT1'][u'host']}"
1285 papi_exec.get_replies(err_msg)
1286 # Add protection for tunnels with IPSEC
1287 cmd = u"ipsec_tunnel_protect_update"
1290 via_label=MPLS_LABEL_INVALID,
1291 obj_id=Constants.BITWISE_NON_ZERO
1293 ipsec_tunnel_protect = dict(
1301 tunnel=ipsec_tunnel_protect
1303 for i in range(existing_tunnels, n_tunnels):
1304 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1305 args[u"tunnel"][u"sa_out"] = i
1306 args[u"tunnel"][u"sa_in"] = [100000 + i]
1308 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1310 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1311 f"on host {nodes[u'DUT1'][u'host']}"
1312 papi_exec.get_replies(err_msg)
1314 # Configure unnumbered interfaces
1315 cmd = u"sw_interface_set_unnumbered"
1318 sw_if_index=InterfaceUtil.get_interface_index(
1319 nodes[u"DUT1"], if1_key
1321 unnumbered_sw_if_index=0
1323 for i in range(existing_tunnels, n_tunnels):
1324 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1326 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1329 cmd = u"sw_interface_set_flags"
1332 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1334 for i in range(existing_tunnels, n_tunnels):
1335 args[u"sw_if_index"] = ipip_tunnels[i]
1337 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1339 # Configure IP routes
1340 cmd = u"ip_route_add_del"
1346 for i in range(existing_tunnels, n_tunnels):
1347 args[u"route"] = IPUtil.compose_vpp_route_structure(
1348 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1349 prefix_len=128 if raddr_ip2.version == 6 else 32,
1350 interface=ipip_tunnels[i]
1353 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1355 err_msg = f"Failed to add IP routes on host " \
1356 f"{nodes[u'DUT1'][u'host']}"
1357 papi_exec.get_replies(err_msg)
1362 def _ipsec_create_tunnel_interfaces_dut2_papi(
1363 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1364 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1365 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1367 :param nodes: VPP nodes to create tunnel interfaces.
1368 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1369 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1370 IPv4/IPv6 address (ip2).
1371 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1372 interface key from topology file.
1373 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1374 :param crypto_alg: The encryption algorithm name.
1375 :param ckeys: List of encryption keys.
1376 :param integ_alg: The integrity algorithm name.
1377 :param ikeys: List of integrity keys.
1378 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1379 :param addr_incr: IP / IPv6 address incremental step.
1380 :param existing_tunnels: Number of tunnel interfaces before creation.
1381 Useful mainly for reconf tests. Default 0.
1385 :type n_tunnels: int
1386 :type crypto_alg: CryptoAlg
1388 :type integ_alg: IntegAlg
1390 :type addr_incr: int
1392 :type existing_tunnels: int
1394 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1395 if not existing_tunnels:
1396 # Set IP address on VPP node 2 interface
1397 cmd = u"sw_interface_add_del_address"
1399 sw_if_index=InterfaceUtil.get_interface_index(
1400 nodes[u"DUT2"], if2_key
1404 prefix=IPUtil.create_prefix_object(
1405 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1409 err_msg = f"Failed to set IP address on interface {if2_key} " \
1410 f"on host {nodes[u'DUT2'][u'host']}"
1411 papi_exec.add(cmd, **args).get_reply(err_msg)
1412 # Configure IPIP tunnel interfaces
1413 cmd = u"ipip_add_tunnel"
1415 instance=Constants.BITWISE_NON_ZERO,
1420 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1422 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1423 dscp=int(IpDscp.IP_API_DSCP_CS0)
1428 ipip_tunnels = [None] * existing_tunnels
1429 for i in range(existing_tunnels, n_tunnels):
1430 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1433 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1434 tun_ips[u"ip1"] + i * addr_incr
1437 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1439 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1440 f" {nodes[u'DUT2'][u'host']}"
1441 ipip_tunnels.extend(
1443 reply[u"sw_if_index"]
1444 for reply in papi_exec.get_replies(err_msg)
1445 if u"sw_if_index" in reply
1448 # Configure IPSec SAD entries
1449 cmd = u"ipsec_sad_entry_add_del_v2"
1461 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1463 crypto_algorithm=crypto_alg.alg_int_repr,
1465 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1466 integrity_key=i_key,
1472 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1474 dscp=int(IpDscp.IP_API_DSCP_CS0),
1477 udp_src_port=IPSEC_UDP_PORT_NONE,
1478 udp_dst_port=IPSEC_UDP_PORT_NONE
1484 for i in range(existing_tunnels, n_tunnels):
1486 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1490 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1492 # SAD entry for outband / tx path
1493 args[u"entry"][u"sad_id"] = 100000 + i
1494 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1496 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1497 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1499 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1500 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1501 args[u"entry"][u"flags"] = int(
1502 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1505 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1507 # SAD entry for inband / rx path
1508 args[u"entry"][u"sad_id"] = i
1509 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1511 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1512 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1514 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1515 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1516 args[u"entry"][u"flags"] = int(
1517 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1518 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1521 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1523 err_msg = f"Failed to add IPsec SAD entries on host" \
1524 f" {nodes[u'DUT2'][u'host']}"
1525 papi_exec.get_replies(err_msg)
1526 # Add protection for tunnels with IPSEC
1527 cmd = u"ipsec_tunnel_protect_update"
1530 via_label=MPLS_LABEL_INVALID,
1531 obj_id=Constants.BITWISE_NON_ZERO
1533 ipsec_tunnel_protect = dict(
1541 tunnel=ipsec_tunnel_protect
1543 for i in range(existing_tunnels, n_tunnels):
1544 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1545 args[u"tunnel"][u"sa_out"] = 100000 + i
1546 args[u"tunnel"][u"sa_in"] = [i]
1548 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1550 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1551 f"on host {nodes[u'DUT2'][u'host']}"
1552 papi_exec.get_replies(err_msg)
1554 if not existing_tunnels:
1555 # Configure IP route
1556 cmd = u"ip_route_add_del"
1557 route = IPUtil.compose_vpp_route_structure(
1558 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1559 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1561 gateway=(tun_ips[u"ip2"] - 1).compressed
1568 papi_exec.add(cmd, **args)
1569 # Configure unnumbered interfaces
1570 cmd = u"sw_interface_set_unnumbered"
1573 sw_if_index=InterfaceUtil.get_interface_index(
1574 nodes[u"DUT2"], if2_key
1576 unnumbered_sw_if_index=0
1578 for i in range(existing_tunnels, n_tunnels):
1579 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1581 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1584 cmd = u"sw_interface_set_flags"
1587 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1589 for i in range(existing_tunnels, n_tunnels):
1590 args[u"sw_if_index"] = ipip_tunnels[i]
1592 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1594 # Configure IP routes
1595 cmd = u"ip_route_add_del"
1601 for i in range(existing_tunnels, n_tunnels):
1602 args[u"route"] = IPUtil.compose_vpp_route_structure(
1603 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1604 prefix_len=128 if raddr_ip1.version == 6 else 32,
1605 interface=ipip_tunnels[i]
1608 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1610 err_msg = f"Failed to add IP routes " \
1611 f"on host {nodes[u'DUT2'][u'host']}"
1612 papi_exec.get_replies(err_msg)
1615 def vpp_ipsec_create_tunnel_interfaces(
1616 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1617 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1618 existing_tunnels=0):
1619 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1621 :param nodes: VPP nodes to create tunnel interfaces.
1622 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1624 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1626 :param if1_key: VPP node 1 interface key from topology file.
1627 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1628 interface key from topology file.
1629 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1630 :param crypto_alg: The encryption algorithm name.
1631 :param integ_alg: The integrity algorithm name.
1632 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1633 first tunnel in direction node1->node2.
1634 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1635 first tunnel in direction node2->node1.
1636 :param raddr_range: Mask specifying range of Policy selector Remote
1637 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1638 and to 128 in case of IPv6.
1639 :param existing_tunnels: Number of tunnel interfaces before creation.
1640 Useful mainly for reconf tests. Default 0.
1642 :type tun_if1_ip_addr: str
1643 :type tun_if2_ip_addr: str
1646 :type n_tunnels: int
1647 :type crypto_alg: CryptoAlg
1648 :type integ_alg: IntegAlg
1649 :type raddr_ip1: string
1650 :type raddr_ip2: string
1651 :type raddr_range: int
1652 :type existing_tunnels: int
1654 n_tunnels = int(n_tunnels)
1655 existing_tunnels = int(existing_tunnels)
1661 ip1=ip_address(tun_if1_ip_addr),
1662 ip2=ip_address(tun_if2_ip_addr)
1664 raddr_ip1 = ip_address(raddr_ip1)
1665 raddr_ip2 = ip_address(raddr_ip2)
1666 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1667 else 1 << (32 - raddr_range)
1669 if n_tunnels - existing_tunnels > 10:
1670 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1671 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1672 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1674 if u"DUT2" not in nodes.keys():
1675 return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
1676 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1677 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1678 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
1681 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1682 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1683 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1685 if u"DUT2" not in nodes.keys():
1686 return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
1687 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1688 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1689 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
1692 return None, None, None, None
1695 def _create_ipsec_script_files(dut, instances):
1696 """Create script files for configuring IPsec in containers
1698 :param dut: DUT node on which to create the script files
1699 :param instances: number of containers on DUT node
1701 :type instances: int
1704 for cnf in range(0, instances):
1706 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1708 scripts.append(open(script_filename, 'w'))
1712 def _close_and_copy_ipsec_script_files(
1713 dut, nodes, instances, scripts):
1714 """Close created scripts and copy them to containers
1716 :param dut: DUT node on which to create the script files
1717 :param nodes: VPP nodes
1718 :param instances: number of containers on DUT node
1719 :param scripts: dictionary holding the script files
1722 :type instances: int
1725 for cnf in range(0, instances):
1726 scripts[cnf].close()
1728 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1730 scp_node(nodes[dut], script_filename, script_filename)
1734 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1735 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1736 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1737 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1739 :param nodes: VPP nodes to create tunnel interfaces.
1740 :param if1_ip_addr: VPP node 1 interface IP4 address.
1741 :param if2_ip_addr: VPP node 2 interface IP4 address.
1742 :param n_tunnels: Number of tunnell interfaces to create.
1743 :param crypto_alg: The encryption algorithm name.
1744 :param integ_alg: The integrity algorithm name.
1745 :param raddr_ip1: Policy selector remote IPv4 start address for the
1746 first tunnel in direction node1->node2.
1747 :param raddr_ip2: Policy selector remote IPv4 start address for the
1748 first tunnel in direction node2->node1.
1749 :param raddr_range: Mask specifying range of Policy selector Remote
1750 IPv4 addresses. Valid values are from 1 to 32.
1751 :param n_instances: Number of containers.
1753 :type if1_ip_addr: str
1754 :type if2_ip_addr: str
1755 :type n_tunnels: int
1756 :type crypto_alg: CryptoAlg
1757 :type integ_alg: IntegAlg
1758 :type raddr_ip1: string
1759 :type raddr_ip2: string
1760 :type raddr_range: int
1761 :type n_instances: int
1765 addr_incr = 1 << (32 - raddr_range)
1767 dut1_scripts = IPsecUtil._create_ipsec_script_files(
1768 u"DUT1", n_instances
1770 dut2_scripts = IPsecUtil._create_ipsec_script_files(
1771 u"DUT2", n_instances
1774 for cnf in range(0, n_instances):
1775 dut1_scripts[cnf].write(
1776 u"create loopback interface\n"
1777 u"set interface state loop0 up\n\n"
1779 dut2_scripts[cnf].write(
1780 f"ip route add {if1_ip_addr}/8 via "
1781 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1784 for tnl in range(0, n_tunnels):
1785 cnf = tnl % n_instances
1787 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1792 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1795 f"integ-alg {integ_alg.alg_name} "
1796 f"local-integ-key {ikey} "
1797 f"remote-integ-key {ikey} "
1799 # Configure tunnel end point(s) on left side
1800 dut1_scripts[cnf].write(
1801 u"set interface ip address loop0 "
1802 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1803 f"create ipsec tunnel "
1804 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1805 f"local-spi {spi_1 + tnl} "
1806 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1807 f"remote-spi {spi_2 + tnl} "
1808 f"crypto-alg {crypto_alg.alg_name} "
1809 f"local-crypto-key {ckey} "
1810 f"remote-crypto-key {ckey} "
1811 f"instance {tnl // n_instances} "
1814 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1815 f"set interface state ipip{tnl // n_instances} up\n"
1816 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1817 f"via ipip{tnl // n_instances}\n\n"
1819 # Configure tunnel end point(s) on right side
1820 dut2_scripts[cnf].write(
1821 f"set ip neighbor memif1/{cnf + 1} "
1822 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1823 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1824 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1825 f"local-spi {spi_2 + tnl} "
1826 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1827 f"remote-spi {spi_1 + tnl} "
1828 f"crypto-alg {crypto_alg.alg_name} "
1829 f"local-crypto-key {ckey} "
1830 f"remote-crypto-key {ckey} "
1831 f"instance {tnl // n_instances} "
1834 f"set interface unnumbered ipip{tnl // n_instances} "
1835 f"use memif1/{cnf + 1}\n"
1836 f"set interface state ipip{tnl // n_instances} up\n"
1837 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1838 f"via ipip{tnl // n_instances}\n\n"
1841 IPsecUtil._close_and_copy_ipsec_script_files(
1842 u"DUT1", nodes, n_instances, dut1_scripts)
1843 IPsecUtil._close_and_copy_ipsec_script_files(
1844 u"DUT2", nodes, n_instances, dut2_scripts)
1847 def vpp_ipsec_add_multiple_tunnels(
1848 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1849 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1850 """Create multiple IPsec tunnels between two VPP nodes.
1852 :param nodes: VPP nodes to create tunnels.
1853 :param interface1: Interface name or sw_if_index on node 1.
1854 :param interface2: Interface name or sw_if_index on node 2.
1855 :param n_tunnels: Number of tunnels to create.
1856 :param crypto_alg: The encryption algorithm name.
1857 :param integ_alg: The integrity algorithm name.
1858 :param tunnel_ip1: Tunnel node1 IPv4 address.
1859 :param tunnel_ip2: Tunnel node2 IPv4 address.
1860 :param raddr_ip1: Policy selector remote IPv4 start address for the
1861 first tunnel in direction node1->node2.
1862 :param raddr_ip2: Policy selector remote IPv4 start address for the
1863 first tunnel in direction node2->node1.
1864 :param raddr_range: Mask specifying range of Policy selector Remote
1865 IPv4 addresses. Valid values are from 1 to 32.
1867 :type interface1: str or int
1868 :type interface2: str or int
1869 :type n_tunnels: int
1870 :type crypto_alg: CryptoAlg
1871 :type integ_alg: IntegAlg
1872 :type tunnel_ip1: str
1873 :type tunnel_ip2: str
1874 :type raddr_ip1: string
1875 :type raddr_ip2: string
1876 :type raddr_range: int
1886 crypto_key = gen_key(
1887 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1889 integ_key = gen_key(
1890 IPsecUtil.get_integ_alg_key_len(integ_alg)
1891 ).decode() if integ_alg else u""
1893 IPsecUtil.vpp_ipsec_set_ip_route(
1894 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1895 interface1, raddr_range)
1896 IPsecUtil.vpp_ipsec_set_ip_route(
1897 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1898 interface2, raddr_range)
1900 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1901 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1902 IPsecUtil.vpp_ipsec_policy_add(
1903 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1904 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1906 IPsecUtil.vpp_ipsec_policy_add(
1907 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1908 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1911 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1912 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1913 IPsecUtil.vpp_ipsec_policy_add(
1914 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1915 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1917 IPsecUtil.vpp_ipsec_policy_add(
1918 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1919 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1922 IPsecUtil.vpp_ipsec_add_sad_entries(
1923 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1924 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1926 IPsecUtil.vpp_ipsec_spd_add_entries(
1927 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1930 IPsecUtil.vpp_ipsec_add_sad_entries(
1931 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1932 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1934 IPsecUtil.vpp_ipsec_spd_add_entries(
1935 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1938 IPsecUtil.vpp_ipsec_add_sad_entries(
1939 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1940 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1943 IPsecUtil.vpp_ipsec_spd_add_entries(
1944 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1947 IPsecUtil.vpp_ipsec_add_sad_entries(
1948 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1949 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1952 IPsecUtil.vpp_ipsec_spd_add_entries(
1953 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1957 def vpp_ipsec_show(node):
1958 """Run "show ipsec" debug CLI command.
1960 :param node: Node to run command on.
1963 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
1966 def show_ipsec_security_association(node):
1967 """Show IPSec security association.
1969 :param node: DUT node.
1975 PapiSocketExecutor.dump_and_log(node, cmds)