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 None argument is accepted, returning zero.
248 :param integ_alg: Integrity algorithm.
249 :type integ_alg: Optional[IntegAlg]
250 :returns: Key length.
253 return 0 if integ_alg is None else integ_alg.key_len
256 def get_integ_alg_scapy_name(integ_alg):
257 """Return integrity algorithm scapy name.
259 :param integ_alg: Integrity algorithm.
260 :type integ_alg: IntegAlg
261 :returns: Algorithm scapy name.
264 return integ_alg.scapy_name
267 def ipsec_proto_esp():
268 """Return IPSec protocol ESP.
270 :returns: IPsecProto enum ESP object.
273 return int(IPsecProto.IPSEC_API_PROTO_ESP)
276 def ipsec_proto_ah():
277 """Return IPSec protocol AH.
279 :returns: IPsecProto enum AH object.
282 return int(IPsecProto.IPSEC_API_PROTO_AH)
285 def vpp_ipsec_select_backend(node, protocol, index=1):
286 """Select IPsec backend.
288 :param node: VPP node to select IPsec backend on.
289 :param protocol: IPsec protocol.
290 :param index: Backend index.
292 :type protocol: IPsecProto
294 :raises RuntimeError: If failed to select IPsec backend or if no API
297 cmd = u"ipsec_select_backend"
298 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
303 with PapiSocketExecutor(node) as papi_exec:
304 papi_exec.add(cmd, **args).get_reply(err_msg)
307 def vpp_ipsec_set_async_mode(node, async_enable=1):
308 """Set IPsec async mode on|off.
310 :param node: VPP node to set IPsec async mode.
311 :param async_enable: Async mode on or off.
313 :type async_enable: int
314 :raises RuntimeError: If failed to set IPsec async mode or if no API
317 cmd = u"ipsec_set_async_mode"
318 err_msg = f"Failed to set IPsec async mode on host {node[u'host']}"
320 async_enable=async_enable
322 with PapiSocketExecutor(node) as papi_exec:
323 papi_exec.add(cmd, **args).get_reply(err_msg)
326 def vpp_ipsec_crypto_sw_scheduler_set_worker(
327 node, worker_index, crypto_enable=False):
328 """Enable or disable crypto on specific vpp worker threads.
330 :param node: VPP node to enable or disable crypto for worker threads.
331 :param worker_index: VPP worker thread index.
332 :param crypto_enable: Disable or enable crypto work.
334 :type worker_index: int
335 :type crypto_enable: bool
336 :raises RuntimeError: If failed to enable or disable crypto for worker
337 thread or if no API reply received.
339 cmd = u"crypto_sw_scheduler_set_worker"
340 err_msg = f"Failed to disable/enable crypto for worker thread " \
341 f"on host {node[u'host']}"
343 worker_index=worker_index,
344 crypto_enable=crypto_enable
346 with PapiSocketExecutor(node) as papi_exec:
347 papi_exec.add(cmd, **args).get_reply(err_msg)
350 def vpp_ipsec_add_sad_entry(
351 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
352 integ_key=u"", tunnel_src=None, tunnel_dst=None):
353 """Create Security Association Database entry on the VPP node.
355 :param node: VPP node to add SAD entry on.
356 :param sad_id: SAD entry ID.
357 :param spi: Security Parameter Index of this SAD entry.
358 :param crypto_alg: The encryption algorithm name.
359 :param crypto_key: The encryption key string.
360 :param integ_alg: The integrity algorithm name.
361 :param integ_key: The integrity key string.
362 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
363 specified ESP transport mode is used.
364 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
365 not specified ESP transport mode is used.
369 :type crypto_alg: CryptoAlg
370 :type crypto_key: str
371 :type integ_alg: Optional[IntegAlg]
373 :type tunnel_src: str
374 :type tunnel_dst: str
376 if isinstance(crypto_key, str):
377 crypto_key = crypto_key.encode(encoding=u"utf-8")
378 if isinstance(integ_key, str):
379 integ_key = integ_key.encode(encoding=u"utf-8")
381 length=len(crypto_key),
385 length=len(integ_key),
386 data=integ_key if integ_key else 0
389 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
390 if tunnel_src and tunnel_dst:
391 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
392 src_addr = ip_address(tunnel_src)
393 dst_addr = ip_address(tunnel_dst)
394 if src_addr.version == 6:
396 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
401 cmd = u"ipsec_sad_entry_add_del_v2"
402 err_msg = f"Failed to add Security Association Database entry " \
403 f"on host {node[u'host']}"
407 crypto_algorithm=crypto_alg.alg_int_repr,
409 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
412 tunnel_src=str(src_addr),
413 tunnel_dst=str(dst_addr),
415 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
417 dscp=int(IpDscp.IP_API_DSCP_CS0),
418 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
419 udp_src_port=4500, # default value in api
420 udp_dst_port=4500 # default value in api
426 with PapiSocketExecutor(node) as papi_exec:
427 papi_exec.add(cmd, **args).get_reply(err_msg)
430 def vpp_ipsec_add_sad_entries(
431 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
432 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
433 """Create multiple Security Association Database entries on VPP node.
435 :param node: VPP node to add SAD entry on.
436 :param n_entries: Number of SAD entries to be created.
437 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
439 :param spi: Security Parameter Index of first SAD entry. All subsequent
440 SAD entries will have spi incremented by 1.
441 :param crypto_alg: The encryption algorithm name.
442 :param crypto_key: The encryption key string.
443 :param integ_alg: The integrity algorithm name.
444 :param integ_key: The integrity key string.
445 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
446 specified ESP transport mode is used.
447 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
448 not specified ESP transport mode is used.
453 :type crypto_alg: CryptoAlg
454 :type crypto_key: str
455 :type integ_alg: Optional[IntegAlg]
457 :type tunnel_src: str
458 :type tunnel_dst: str
460 if isinstance(crypto_key, str):
461 crypto_key = crypto_key.encode(encoding=u"utf-8")
462 if isinstance(integ_key, str):
463 integ_key = integ_key.encode(encoding=u"utf-8")
464 if tunnel_src and tunnel_dst:
465 src_addr = ip_address(tunnel_src)
466 dst_addr = ip_address(tunnel_dst)
471 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
474 if int(n_entries) > 10:
475 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
477 with open(tmp_filename, 'w') as tmp_file:
478 for i in range(n_entries):
479 integ = f"integ-alg {integ_alg.alg_name} " \
480 f"integ-key {integ_key.hex()}" \
481 if integ_alg else u""
482 tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
483 f"tunnel-dst {dst_addr + i * addr_incr}" \
484 if tunnel_src and tunnel_dst else u""
485 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
486 f"crypto-alg {crypto_alg.alg_name} " \
487 f"crypto-key {crypto_key.hex()} " \
488 f"{integ} {tunnel}\n"
492 tmp_filename, node, timeout=300, json_out=False,
495 os.remove(tmp_filename)
499 length=len(crypto_key),
503 length=len(integ_key),
504 data=integ_key if integ_key else 0
507 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
508 if tunnel_src and tunnel_dst:
509 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
510 if src_addr.version == 6:
512 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
515 cmd = u"ipsec_sad_entry_add_del_v2"
516 err_msg = f"Failed to add Security Association Database entry " \
517 f"on host {node[u'host']}"
522 crypto_algorithm=crypto_alg.alg_int_repr,
524 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
527 tunnel_src=str(src_addr),
528 tunnel_dst=str(dst_addr),
530 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
532 dscp=int(IpDscp.IP_API_DSCP_CS0),
533 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
534 udp_src_port=4500, # default value in api
535 udp_dst_port=4500 # default value in api
541 with PapiSocketExecutor(node) as papi_exec:
542 for i in range(n_entries):
543 args[u"entry"][u"sad_id"] = int(sad_id) + i
544 args[u"entry"][u"spi"] = int(spi) + i
545 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
546 if tunnel_src and tunnel_dst else src_addr
547 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
548 if tunnel_src and tunnel_dst else dst_addr
549 history = bool(not 1 < i < n_entries - 2)
550 papi_exec.add(cmd, history=history, **args)
551 papi_exec.get_replies(err_msg)
554 def vpp_ipsec_set_ip_route(
555 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
557 """Set IP address and route on interface.
559 :param node: VPP node to add config on.
560 :param n_tunnels: Number of tunnels to create.
561 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
562 :param traffic_addr: Traffic destination IP address to route.
563 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
564 :param interface: Interface key on node 1.
565 :param raddr_range: Mask specifying range of Policy selector Remote IP
566 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
570 :type tunnel_src: str
571 :type traffic_addr: str
572 :type tunnel_dst: str
574 :type raddr_range: int
576 tunnel_src = ip_address(tunnel_src)
577 tunnel_dst = ip_address(tunnel_dst)
578 traffic_addr = ip_address(traffic_addr)
579 addr_incr = 1 << (128 - raddr_range) if tunnel_src.version == 6 \
580 else 1 << (32 - raddr_range)
582 if int(n_tunnels) > 10:
583 tmp_filename = u"/tmp/ipsec_set_ip.script"
585 with open(tmp_filename, 'w') as tmp_file:
586 if_name = Topology.get_interface_name(node, interface)
587 for i in range(n_tunnels):
588 conf = f"exec set interface ip address {if_name} " \
589 f"{tunnel_src + i * addr_incr}/{raddr_range}\n" \
590 f"exec ip route add {traffic_addr + i}/" \
591 f"{128 if traffic_addr.version == 6 else 32} " \
592 f"via {tunnel_dst + i * addr_incr} {if_name}\n"
594 VatExecutor().execute_script(
595 tmp_filename, node, timeout=300, json_out=False,
598 os.remove(tmp_filename)
601 cmd1 = u"sw_interface_add_del_address"
603 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
608 cmd2 = u"ip_route_add_del"
614 err_msg = f"Failed to configure IP addresses and IP routes " \
615 f"on interface {interface} on host {node[u'host']}"
617 with PapiSocketExecutor(node) as papi_exec:
618 for i in range(n_tunnels):
619 args1[u"prefix"] = IPUtil.create_prefix_object(
620 tunnel_src + i * addr_incr, raddr_range
622 args2[u"route"] = IPUtil.compose_vpp_route_structure(
623 node, traffic_addr + i,
624 prefix_len=128 if traffic_addr.version == 6 else 32,
625 interface=interface, gateway=tunnel_dst + i * addr_incr
627 history = bool(not 1 < i < n_tunnels - 2)
628 papi_exec.add(cmd1, history=history, **args1).\
629 add(cmd2, history=history, **args2)
630 papi_exec.get_replies(err_msg)
633 def vpp_ipsec_add_spd(node, spd_id):
634 """Create Security Policy Database on the VPP node.
636 :param node: VPP node to add SPD on.
637 :param spd_id: SPD ID.
641 cmd = u"ipsec_spd_add_del"
642 err_msg = f"Failed to add Security Policy Database " \
643 f"on host {node[u'host']}"
648 with PapiSocketExecutor(node) as papi_exec:
649 papi_exec.add(cmd, **args).get_reply(err_msg)
652 def vpp_ipsec_spd_add_if(node, spd_id, interface):
653 """Add interface to the Security Policy Database.
655 :param node: VPP node.
656 :param spd_id: SPD ID to add interface on.
657 :param interface: Interface name or sw_if_index.
660 :type interface: str or int
662 cmd = u"ipsec_interface_add_del_spd"
663 err_msg = f"Failed to add interface {interface} to Security Policy " \
664 f"Database {spd_id} on host {node[u'host']}"
667 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
670 with PapiSocketExecutor(node) as papi_exec:
671 papi_exec.add(cmd, **args).get_reply(err_msg)
674 def vpp_ipsec_policy_add(
675 node, spd_id, priority, action, inbound=True, sa_id=None,
676 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
677 rport_range=None, is_ipv6=False):
678 """Create Security Policy Database entry on the VPP node.
680 :param node: VPP node to add SPD entry on.
681 :param spd_id: SPD ID to add entry on.
682 :param priority: SPD entry priority, higher number = higher priority.
683 :param action: Policy action.
684 :param inbound: If True policy is for inbound traffic, otherwise
686 :param sa_id: SAD entry ID for protect action.
687 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
688 format IP/prefix or IP/mask. If no mask is provided,
689 it's considered to be /32.
690 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
691 format IP/prefix or IP/mask. If no mask is provided,
692 it's considered to be /32.
693 :param proto: Policy selector next layer protocol number.
694 :param lport_range: Policy selector local TCP/UDP port range in format
695 <port_start>-<port_end>.
696 :param rport_range: Policy selector remote TCP/UDP port range in format
697 <port_start>-<port_end>.
698 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
699 not defined so it will default to address ::/0, otherwise False.
703 :type action: PolicyAction
706 :type laddr_range: string
707 :type raddr_range: string
709 :type lport_range: string
710 :type rport_range: string
713 if laddr_range is None:
714 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
716 if raddr_range is None:
717 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
719 cmd = u"ipsec_spd_entry_add_del"
720 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
721 f"on host {node[u'host']}"
725 priority=int(priority),
726 is_outbound=not inbound,
727 sa_id=int(sa_id) if sa_id else 0,
728 policy=action.policy_int_repr,
729 protocol=int(proto) if proto else 0,
730 remote_address_start=IPAddress.create_ip_address_object(
731 ip_network(raddr_range, strict=False).network_address
733 remote_address_stop=IPAddress.create_ip_address_object(
734 ip_network(raddr_range, strict=False).broadcast_address
736 local_address_start=IPAddress.create_ip_address_object(
737 ip_network(laddr_range, strict=False).network_address
739 local_address_stop=IPAddress.create_ip_address_object(
740 ip_network(laddr_range, strict=False).broadcast_address
742 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
744 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
746 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
748 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
755 with PapiSocketExecutor(node) as papi_exec:
756 papi_exec.add(cmd, **args).get_reply(err_msg)
759 def vpp_ipsec_spd_add_entries(
760 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
762 """Create multiple Security Policy Database entries on the VPP node.
764 :param node: VPP node to add SPD entries on.
765 :param n_entries: Number of SPD entries to be added.
766 :param spd_id: SPD ID to add entries on.
767 :param priority: SPD entries priority, higher number = higher priority.
768 :param inbound: If True policy is for inbound traffic, otherwise
770 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
771 SAD entry ID incremented by 1.
772 :param raddr_ip: Policy selector remote IPv4 start address for the first
773 entry. Remote IPv4 end address will be calculated depending on
774 raddr_range parameter. Each subsequent entry will have start address
775 next after IPv4 end address of previous entry.
776 :param raddr_range: Required IP addres range.
784 :type raddr_range: int
786 raddr_ip = ip_address(raddr_ip)
787 if int(n_entries) > 10:
788 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
790 with open(tmp_filename, 'w') as tmp_file:
791 for i in range(n_entries):
792 direction = u'inbound' if inbound else u'outbound'
793 tunnel = f"exec ipsec policy add spd {spd_id} " \
794 f"priority {priority} {direction} " \
795 f"action protect sa {sa_id+i} " \
796 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
797 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
798 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
799 tmp_file.write(tunnel)
800 VatExecutor().execute_script(
801 tmp_filename, node, timeout=300, json_out=False,
804 os.remove(tmp_filename)
807 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
809 cmd = u"ipsec_spd_entry_add_del"
810 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
811 f"on host {node[u'host']}"
815 priority=int(priority),
816 is_outbound=not inbound,
817 sa_id=int(sa_id) if sa_id else 0,
818 policy=getattr(PolicyAction.PROTECT, u"policy_int_repr"),
820 remote_address_start=IPAddress.create_ip_address_object(raddr_ip),
821 remote_address_stop=IPAddress.create_ip_address_object(raddr_ip),
822 local_address_start=IPAddress.create_ip_address_object(
823 ip_network(laddr_range, strict=False).network_address
825 local_address_stop=IPAddress.create_ip_address_object(
826 ip_network(laddr_range, strict=False).broadcast_address
829 remote_port_stop=65535,
831 local_port_stop=65535
838 with PapiSocketExecutor(node) as papi_exec:
839 for i in range(n_entries):
840 args[u"entry"][u"remote_address_start"][u"un"] = \
841 IPAddress.union_addr(raddr_ip + i)
842 args[u"entry"][u"remote_address_stop"][u"un"] = \
843 IPAddress.union_addr(raddr_ip + i)
844 history = bool(not 1 < i < n_entries - 2)
845 papi_exec.add(cmd, history=history, **args)
846 papi_exec.get_replies(err_msg)
849 def _ipsec_create_tunnel_interfaces_dut1_vat(
850 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
851 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
852 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
854 Generate random keys and return them (so DUT2 or TG can decrypt).
856 :param nodes: VPP nodes to create tunnel interfaces.
857 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
858 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
859 IPv4/IPv6 address (ip2).
860 :param if1_key: VPP node 1 interface key from topology file.
861 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
862 interface key from topology file.
863 :param n_tunnels: Number of tunnel interfaces to be there at the end.
864 :param crypto_alg: The encryption algorithm name.
865 :param integ_alg: The integrity algorithm name.
866 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
867 first tunnel in direction node2->node1.
868 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
869 :param addr_incr: IP / IPv6 address incremental step.
870 :param existing_tunnels: Number of tunnel interfaces before creation.
871 Useful mainly for reconf tests. Default 0.
877 :type crypto_alg: CryptoAlg
878 :type integ_alg: Optional[IntegAlg]
879 :type raddr_ip2: IPv4Address or IPv6Address
882 :type existing_tunnels: int
883 :returns: Generated ckeys and ikeys.
884 :rtype: List[bytes], List[bytes]
886 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
887 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
889 ckeys = [bytes()] * existing_tunnels
890 ikeys = [bytes()] * existing_tunnels
893 with open(tmp_fn1, u"w") as tmp_f1:
894 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
895 if u"DUT2" in nodes.keys() \
896 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
897 if not existing_tunnels:
899 f"exec create loopback interface\n"
900 f"exec set interface state loop0 up\n"
901 f"exec set interface ip address {if1_n} "
902 f"{tun_ips[u'ip2'] - 1}/"
903 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
904 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
907 for i in range(existing_tunnels, n_tunnels):
909 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
912 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
915 integ = f"integ-alg {integ_alg.alg_name} " \
916 f"integ-key {ikeys[i].hex()} "
920 f"exec set interface ip address loop0 "
921 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
922 f"exec create ipip tunnel "
923 f"src {tun_ips[u'ip1'] + i * addr_incr} "
924 f"dst {tun_ips[u'ip2']} "
926 f"exec ipsec sa add {i} "
927 f"spi {spi_d[u'spi_1'] + i} "
928 f"crypto-alg {crypto_alg.alg_name} "
929 f"crypto-key {ckeys[i].hex()} "
932 f"exec ipsec sa add {100000 + i} "
933 f"spi {spi_d[u'spi_2'] + i} "
934 f"crypto-alg {crypto_alg.alg_name} "
935 f"crypto-key {ckeys[i].hex()} "
938 f"exec ipsec tunnel protect ipip{i} "
940 f"sa-in {100000 + i} "
944 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
945 copy_on_execute=True,
946 history=bool(n_tunnels < 100)
950 with open(tmp_fn1, 'w') as tmp_f1:
951 for i in range(existing_tunnels, n_tunnels):
953 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
954 f"exec set interface state ipip{i} up\n"
955 f"exec ip route add "
956 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
960 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
961 copy_on_execute=True,
962 history=bool(n_tunnels < 100)
969 def _ipsec_create_tunnel_interfaces_dut2_vat(
970 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
971 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
972 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
974 This method accesses keys generated by DUT1 method
975 and does not return anything.
977 :param nodes: VPP nodes to create tunnel interfaces.
978 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
979 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
980 IPv4/IPv6 address (ip2).
981 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
982 interface key from topology file.
983 :param n_tunnels: Number of tunnel interfaces to be there at the end.
984 :param crypto_alg: The encryption algorithm name.
985 :param ckeys: List of encryption keys.
986 :param integ_alg: The integrity algorithm name.
987 :param ikeys: List of integrity keys.
988 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
989 :param addr_incr: IP / IPv6 address incremental step.
990 :param existing_tunnels: Number of tunnel interfaces before creation.
991 Useful mainly for reconf tests. Default 0.
996 :type crypto_alg: CryptoAlg
997 :type ckeys: Sequence[bytes]
998 :type integ_alg: Optional[IntegAlg]
999 :type ikeys: Sequence[bytes]
1000 :type addr_incr: int
1002 :type existing_tunnels: int
1004 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1005 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1008 with open(tmp_fn2, 'w') as tmp_f2:
1009 if not existing_tunnels:
1011 f"exec set interface ip address {if2_n}"
1012 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1014 for i in range(existing_tunnels, n_tunnels):
1016 integ = f"integ-alg {integ_alg.alg_name} " \
1017 f"integ-key {ikeys[i].hex()} "
1021 f"exec create ipip tunnel "
1022 f"src {tun_ips[u'ip2']} "
1023 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1025 f"exec ipsec sa add {100000 + i} "
1026 f"spi {spi_d[u'spi_2'] + i} "
1027 f"crypto-alg {crypto_alg.alg_name} "
1028 f"crypto-key {ckeys[i].hex()} "
1031 f"exec ipsec sa add {i} "
1032 f"spi {spi_d[u'spi_1'] + i} "
1033 f"crypto-alg {crypto_alg.alg_name} "
1034 f"crypto-key {ckeys[i].hex()} "
1037 f"exec ipsec tunnel protect ipip{i} "
1038 f"sa-out {100000 + i} "
1043 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1044 copy_on_execute=True,
1045 history=bool(n_tunnels < 100)
1049 with open(tmp_fn2, 'w') as tmp_f2:
1050 if not existing_tunnels:
1052 f"exec ip route add {tun_ips[u'ip1']}/8 "
1053 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1055 for i in range(existing_tunnels, n_tunnels):
1057 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1058 f"exec set interface state ipip{i} up\n"
1059 f"exec ip route add "
1060 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1064 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1065 copy_on_execute=True,
1066 history=bool(n_tunnels < 100)
1071 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1072 """Create loopback interface and set IP address on VPP node 1 interface
1075 :param nodes: VPP nodes to create tunnel interfaces.
1076 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1077 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1078 IPv4/IPv6 address (ip2).
1079 :param if1_key: VPP node 1 interface key from topology file.
1080 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1081 interface key from topology file.
1087 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1088 # Create loopback interface on DUT1, set it to up state
1089 cmd = u"create_loopback_instance"
1095 err_msg = f"Failed to create loopback interface " \
1096 f"on host {nodes[u'DUT1'][u'host']}"
1097 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1098 get_sw_if_index(err_msg)
1099 cmd = u"sw_interface_set_flags"
1101 sw_if_index=loop_sw_if_idx,
1102 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1104 err_msg = f"Failed to set loopback interface state up " \
1105 f"on host {nodes[u'DUT1'][u'host']}"
1106 papi_exec.add(cmd, **args).get_reply(err_msg)
1107 # Set IP address on VPP node 1 interface
1108 cmd = u"sw_interface_add_del_address"
1110 sw_if_index=InterfaceUtil.get_interface_index(
1111 nodes[u"DUT1"], if1_key
1115 prefix=IPUtil.create_prefix_object(
1116 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1120 err_msg = f"Failed to set IP address on interface {if1_key} " \
1121 f"on host {nodes[u'DUT1'][u'host']}"
1122 papi_exec.add(cmd, **args).get_reply(err_msg)
1123 cmd2 = u"ip_neighbor_add_del"
1127 sw_if_index=Topology.get_interface_sw_index(
1128 nodes[u"DUT1"], if1_key
1132 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1133 if u"DUT2" in nodes.keys()
1134 else Topology.get_interface_mac(
1135 nodes[u"TG"], if2_key
1138 ip_address=tun_ips[u"ip2"].compressed
1141 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1142 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1144 return loop_sw_if_idx
1147 def _ipsec_create_tunnel_interfaces_dut1_papi(
1148 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1149 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1150 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1152 Generate random keys and return them (so DUT2 or TG can decrypt).
1154 :param nodes: VPP nodes to create tunnel interfaces.
1155 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1156 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1157 IPv4/IPv6 address (ip2).
1158 :param if1_key: VPP node 1 interface key from topology file.
1159 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1160 interface key from topology file.
1161 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1162 :param crypto_alg: The encryption algorithm name.
1163 :param integ_alg: The integrity algorithm name.
1164 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1165 first tunnel in direction node2->node1.
1166 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1167 :param addr_incr: IP / IPv6 address incremental step.
1168 :param existing_tunnels: Number of tunnel interfaces before creation.
1169 Useful mainly for reconf tests. Default 0.
1174 :type n_tunnels: int
1175 :type crypto_alg: CryptoAlg
1176 :type integ_alg: Optional[IntegAlg]
1177 :type raddr_ip2: IPv4Address or IPv6Address
1178 :type addr_incr: int
1180 :type existing_tunnels: int
1181 :returns: Generated ckeys and ikeys.
1182 :rtype: List[bytes], List[bytes]
1184 if not existing_tunnels:
1185 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1186 nodes, tun_ips, if1_key, if2_key
1189 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1190 nodes[u"DUT1"], u"loop0"
1192 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1193 # Configure IP addresses on loop0 interface
1194 cmd = u"sw_interface_add_del_address"
1196 sw_if_index=loop_sw_if_idx,
1201 for i in range(existing_tunnels, n_tunnels):
1202 args[u"prefix"] = IPUtil.create_prefix_object(
1203 tun_ips[u"ip1"] + i * addr_incr,
1204 128 if tun_ips[u"ip1"].version == 6 else 32
1207 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1209 # Configure IPIP tunnel interfaces
1210 cmd = u"ipip_add_tunnel"
1212 instance=Constants.BITWISE_NON_ZERO,
1217 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1219 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1220 dscp=int(IpDscp.IP_API_DSCP_CS0)
1225 ipip_tunnels = [None] * existing_tunnels
1226 for i in range(existing_tunnels, n_tunnels):
1227 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1228 tun_ips[u"ip1"] + i * addr_incr
1230 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1234 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1236 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1237 f" {nodes[u'DUT1'][u'host']}"
1238 ipip_tunnels.extend(
1240 reply[u"sw_if_index"]
1241 for reply in papi_exec.get_replies(err_msg)
1242 if u"sw_if_index" in reply
1245 # Configure IPSec SAD entries
1246 ckeys = [bytes()] * existing_tunnels
1247 ikeys = [bytes()] * existing_tunnels
1248 cmd = u"ipsec_sad_entry_add_del_v2"
1260 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1261 crypto_algorithm=crypto_alg.alg_int_repr,
1263 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1264 integrity_key=i_key,
1269 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1271 dscp=int(IpDscp.IP_API_DSCP_CS0),
1274 udp_src_port=IPSEC_UDP_PORT_NONE,
1275 udp_dst_port=IPSEC_UDP_PORT_NONE
1281 for i in range(existing_tunnels, n_tunnels):
1283 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1286 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1288 # SAD entry for outband / tx path
1289 args[u"entry"][u"sad_id"] = i
1290 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1292 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1293 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1295 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1296 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1297 args[u"entry"][u"flags"] = int(
1298 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1301 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1303 # SAD entry for inband / rx path
1304 args[u"entry"][u"sad_id"] = 100000 + i
1305 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1307 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1308 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1310 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1311 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1312 args[u"entry"][u"flags"] = int(
1313 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1314 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1317 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1319 err_msg = f"Failed to add IPsec SAD entries on host" \
1320 f" {nodes[u'DUT1'][u'host']}"
1321 papi_exec.get_replies(err_msg)
1322 # Add protection for tunnels with IPSEC
1323 cmd = u"ipsec_tunnel_protect_update"
1326 via_label=MPLS_LABEL_INVALID,
1327 obj_id=Constants.BITWISE_NON_ZERO
1329 ipsec_tunnel_protect = dict(
1337 tunnel=ipsec_tunnel_protect
1339 for i in range(existing_tunnels, n_tunnels):
1340 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1341 args[u"tunnel"][u"sa_out"] = i
1342 args[u"tunnel"][u"sa_in"] = [100000 + i]
1344 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1346 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1347 f"on host {nodes[u'DUT1'][u'host']}"
1348 papi_exec.get_replies(err_msg)
1350 # Configure unnumbered interfaces
1351 cmd = u"sw_interface_set_unnumbered"
1354 sw_if_index=InterfaceUtil.get_interface_index(
1355 nodes[u"DUT1"], if1_key
1357 unnumbered_sw_if_index=0
1359 for i in range(existing_tunnels, n_tunnels):
1360 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1362 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1365 cmd = u"sw_interface_set_flags"
1368 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1370 for i in range(existing_tunnels, n_tunnels):
1371 args[u"sw_if_index"] = ipip_tunnels[i]
1373 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1375 # Configure IP routes
1376 cmd = u"ip_route_add_del"
1382 for i in range(existing_tunnels, n_tunnels):
1383 args[u"route"] = IPUtil.compose_vpp_route_structure(
1384 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1385 prefix_len=128 if raddr_ip2.version == 6 else 32,
1386 interface=ipip_tunnels[i]
1389 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1391 err_msg = f"Failed to add IP routes on host " \
1392 f"{nodes[u'DUT1'][u'host']}"
1393 papi_exec.get_replies(err_msg)
1398 def _ipsec_create_tunnel_interfaces_dut2_papi(
1399 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1400 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1401 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1403 This method accesses keys generated by DUT1 method
1404 and does not return anything.
1406 :param nodes: VPP nodes to create tunnel interfaces.
1407 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1408 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1409 IPv4/IPv6 address (ip2).
1410 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1411 interface key from topology file.
1412 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1413 :param crypto_alg: The encryption algorithm name.
1414 :param ckeys: List of encryption keys.
1415 :param integ_alg: The integrity algorithm name.
1416 :param ikeys: List of integrity keys.
1417 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1418 :param addr_incr: IP / IPv6 address incremental step.
1419 :param existing_tunnels: Number of tunnel interfaces before creation.
1420 Useful mainly for reconf tests. Default 0.
1424 :type n_tunnels: int
1425 :type crypto_alg: CryptoAlg
1426 :type ckeys: Sequence[bytes]
1427 :type integ_alg: Optional[IntegAlg]
1428 :type ikeys: Sequence[bytes]
1429 :type addr_incr: int
1431 :type existing_tunnels: int
1433 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1434 if not existing_tunnels:
1435 # Set IP address on VPP node 2 interface
1436 cmd = u"sw_interface_add_del_address"
1438 sw_if_index=InterfaceUtil.get_interface_index(
1439 nodes[u"DUT2"], if2_key
1443 prefix=IPUtil.create_prefix_object(
1444 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1448 err_msg = f"Failed to set IP address on interface {if2_key} " \
1449 f"on host {nodes[u'DUT2'][u'host']}"
1450 papi_exec.add(cmd, **args).get_reply(err_msg)
1451 # Configure IPIP tunnel interfaces
1452 cmd = u"ipip_add_tunnel"
1454 instance=Constants.BITWISE_NON_ZERO,
1459 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1461 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1462 dscp=int(IpDscp.IP_API_DSCP_CS0)
1467 ipip_tunnels = [None] * existing_tunnels
1468 for i in range(existing_tunnels, n_tunnels):
1469 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1472 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1473 tun_ips[u"ip1"] + i * addr_incr
1476 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1478 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1479 f" {nodes[u'DUT2'][u'host']}"
1480 ipip_tunnels.extend(
1482 reply[u"sw_if_index"]
1483 for reply in papi_exec.get_replies(err_msg)
1484 if u"sw_if_index" in reply
1487 # Configure IPSec SAD entries
1488 cmd = u"ipsec_sad_entry_add_del_v2"
1500 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1502 crypto_algorithm=crypto_alg.alg_int_repr,
1504 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1505 integrity_key=i_key,
1511 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1513 dscp=int(IpDscp.IP_API_DSCP_CS0),
1516 udp_src_port=IPSEC_UDP_PORT_NONE,
1517 udp_dst_port=IPSEC_UDP_PORT_NONE
1523 for i in range(existing_tunnels, n_tunnels):
1525 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1528 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1530 # SAD entry for outband / tx path
1531 args[u"entry"][u"sad_id"] = 100000 + i
1532 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1534 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1535 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1537 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1538 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1539 args[u"entry"][u"flags"] = int(
1540 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1543 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1545 # SAD entry for inband / rx path
1546 args[u"entry"][u"sad_id"] = i
1547 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1549 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1550 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1552 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1553 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1554 args[u"entry"][u"flags"] = int(
1555 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1556 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1559 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1561 err_msg = f"Failed to add IPsec SAD entries on host" \
1562 f" {nodes[u'DUT2'][u'host']}"
1563 papi_exec.get_replies(err_msg)
1564 # Add protection for tunnels with IPSEC
1565 cmd = u"ipsec_tunnel_protect_update"
1568 via_label=MPLS_LABEL_INVALID,
1569 obj_id=Constants.BITWISE_NON_ZERO
1571 ipsec_tunnel_protect = dict(
1579 tunnel=ipsec_tunnel_protect
1581 for i in range(existing_tunnels, n_tunnels):
1582 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1583 args[u"tunnel"][u"sa_out"] = 100000 + i
1584 args[u"tunnel"][u"sa_in"] = [i]
1586 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1588 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1589 f"on host {nodes[u'DUT2'][u'host']}"
1590 papi_exec.get_replies(err_msg)
1592 if not existing_tunnels:
1593 # Configure IP route
1594 cmd = u"ip_route_add_del"
1595 route = IPUtil.compose_vpp_route_structure(
1596 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1597 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1599 gateway=(tun_ips[u"ip2"] - 1).compressed
1606 papi_exec.add(cmd, **args)
1607 # Configure unnumbered interfaces
1608 cmd = u"sw_interface_set_unnumbered"
1611 sw_if_index=InterfaceUtil.get_interface_index(
1612 nodes[u"DUT2"], if2_key
1614 unnumbered_sw_if_index=0
1616 for i in range(existing_tunnels, n_tunnels):
1617 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1619 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1622 cmd = u"sw_interface_set_flags"
1625 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1627 for i in range(existing_tunnels, n_tunnels):
1628 args[u"sw_if_index"] = ipip_tunnels[i]
1630 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1632 # Configure IP routes
1633 cmd = u"ip_route_add_del"
1639 for i in range(existing_tunnels, n_tunnels):
1640 args[u"route"] = IPUtil.compose_vpp_route_structure(
1641 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1642 prefix_len=128 if raddr_ip1.version == 6 else 32,
1643 interface=ipip_tunnels[i]
1646 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1648 err_msg = f"Failed to add IP routes " \
1649 f"on host {nodes[u'DUT2'][u'host']}"
1650 papi_exec.get_replies(err_msg)
1653 def vpp_ipsec_create_tunnel_interfaces(
1654 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1655 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1656 existing_tunnels=0, return_keys=False):
1657 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1659 Some deployments (e.g. devicetest) need to know the generated keys.
1660 But other deployments (e.g. scale perf test) would get spammed
1661 if we returned keys every time.
1663 :param nodes: VPP nodes to create tunnel interfaces.
1664 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1666 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1668 :param if1_key: VPP node 1 interface key from topology file.
1669 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1670 interface key from topology file.
1671 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1672 :param crypto_alg: The encryption algorithm name.
1673 :param integ_alg: The integrity algorithm name.
1674 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1675 first tunnel in direction node1->node2.
1676 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1677 first tunnel in direction node2->node1.
1678 :param raddr_range: Mask specifying range of Policy selector Remote
1679 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1680 and to 128 in case of IPv6.
1681 :param existing_tunnels: Number of tunnel interfaces before creation.
1682 Useful mainly for reconf tests. Default 0.
1683 :param return_keys: Whether generated keys should be returned.
1685 :type tun_if1_ip_addr: str
1686 :type tun_if2_ip_addr: str
1689 :type n_tunnels: int
1690 :type crypto_alg: CryptoAlg
1691 :type integ_alg: Optonal[IntegAlg]
1692 :type raddr_ip1: string
1693 :type raddr_ip2: string
1694 :type raddr_range: int
1695 :type existing_tunnels: int
1696 :type return_keys: bool
1697 :returns: Ckeys, ikeys, spi_1, spi_2.
1698 :rtype: Optional[List[bytes], List[bytes], int, int]
1700 n_tunnels = int(n_tunnels)
1701 existing_tunnels = int(existing_tunnels)
1707 ip1=ip_address(tun_if1_ip_addr),
1708 ip2=ip_address(tun_if2_ip_addr)
1710 raddr_ip1 = ip_address(raddr_ip1)
1711 raddr_ip2 = ip_address(raddr_ip2)
1712 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1713 else 1 << (32 - raddr_range)
1715 if n_tunnels - existing_tunnels > 10:
1716 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1717 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1718 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1720 if u"DUT2" in nodes.keys():
1721 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1722 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1723 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1727 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1728 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1729 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1731 if u"DUT2" in nodes.keys():
1732 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1733 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1734 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1739 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1743 def _create_ipsec_script_files(dut, instances):
1744 """Create script files for configuring IPsec in containers
1746 :param dut: DUT node on which to create the script files
1747 :param instances: number of containers on DUT node
1749 :type instances: int
1752 for cnf in range(0, instances):
1754 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1756 scripts.append(open(script_filename, 'w'))
1760 def _close_and_copy_ipsec_script_files(
1761 dut, nodes, instances, scripts):
1762 """Close created scripts and copy them to containers
1764 :param dut: DUT node on which to create the script files
1765 :param nodes: VPP nodes
1766 :param instances: number of containers on DUT node
1767 :param scripts: dictionary holding the script files
1770 :type instances: int
1773 for cnf in range(0, instances):
1774 scripts[cnf].close()
1776 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1778 scp_node(nodes[dut], script_filename, script_filename)
1782 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1783 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1784 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1785 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1787 :param nodes: VPP nodes to create tunnel interfaces.
1788 :param if1_ip_addr: VPP node 1 interface IP4 address.
1789 :param if2_ip_addr: VPP node 2 interface IP4 address.
1790 :param n_tunnels: Number of tunnell interfaces to create.
1791 :param crypto_alg: The encryption algorithm name.
1792 :param integ_alg: The integrity algorithm name.
1793 :param raddr_ip1: Policy selector remote IPv4 start address for the
1794 first tunnel in direction node1->node2.
1795 :param raddr_ip2: Policy selector remote IPv4 start address for the
1796 first tunnel in direction node2->node1.
1797 :param raddr_range: Mask specifying range of Policy selector Remote
1798 IPv4 addresses. Valid values are from 1 to 32.
1799 :param n_instances: Number of containers.
1801 :type if1_ip_addr: str
1802 :type if2_ip_addr: str
1803 :type n_tunnels: int
1804 :type crypto_alg: CryptoAlg
1805 :type integ_alg: Optional[IntegAlg]
1806 :type raddr_ip1: string
1807 :type raddr_ip2: string
1808 :type raddr_range: int
1809 :type n_instances: int
1813 addr_incr = 1 << (32 - raddr_range)
1815 dut1_scripts = IPsecUtil._create_ipsec_script_files(
1816 u"DUT1", n_instances
1818 dut2_scripts = IPsecUtil._create_ipsec_script_files(
1819 u"DUT2", n_instances
1822 for cnf in range(0, n_instances):
1823 dut1_scripts[cnf].write(
1824 u"create loopback interface\n"
1825 u"set interface state loop0 up\n\n"
1827 dut2_scripts[cnf].write(
1828 f"ip route add {if1_ip_addr}/8 via "
1829 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1832 for tnl in range(0, n_tunnels):
1833 cnf = tnl % n_instances
1835 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1839 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1843 f"integ-alg {integ_alg.alg_name} "
1844 f"local-integ-key {ikey} "
1845 f"remote-integ-key {ikey} "
1847 # Configure tunnel end point(s) on left side
1848 dut1_scripts[cnf].write(
1849 u"set interface ip address loop0 "
1850 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1851 f"create ipsec tunnel "
1852 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1853 f"local-spi {spi_1 + tnl} "
1854 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1855 f"remote-spi {spi_2 + tnl} "
1856 f"crypto-alg {crypto_alg.alg_name} "
1857 f"local-crypto-key {ckey} "
1858 f"remote-crypto-key {ckey} "
1859 f"instance {tnl // n_instances} "
1862 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1863 f"set interface state ipip{tnl // n_instances} up\n"
1864 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1865 f"via ipip{tnl // n_instances}\n\n"
1867 # Configure tunnel end point(s) on right side
1868 dut2_scripts[cnf].write(
1869 f"set ip neighbor memif1/{cnf + 1} "
1870 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1871 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1872 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1873 f"local-spi {spi_2 + tnl} "
1874 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1875 f"remote-spi {spi_1 + tnl} "
1876 f"crypto-alg {crypto_alg.alg_name} "
1877 f"local-crypto-key {ckey} "
1878 f"remote-crypto-key {ckey} "
1879 f"instance {tnl // n_instances} "
1882 f"set interface unnumbered ipip{tnl // n_instances} "
1883 f"use memif1/{cnf + 1}\n"
1884 f"set interface state ipip{tnl // n_instances} up\n"
1885 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1886 f"via ipip{tnl // n_instances}\n\n"
1889 IPsecUtil._close_and_copy_ipsec_script_files(
1890 u"DUT1", nodes, n_instances, dut1_scripts)
1891 IPsecUtil._close_and_copy_ipsec_script_files(
1892 u"DUT2", nodes, n_instances, dut2_scripts)
1895 def vpp_ipsec_add_multiple_tunnels(
1896 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1897 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1898 """Create multiple IPsec tunnels between two VPP nodes.
1900 :param nodes: VPP nodes to create tunnels.
1901 :param interface1: Interface name or sw_if_index on node 1.
1902 :param interface2: Interface name or sw_if_index on node 2.
1903 :param n_tunnels: Number of tunnels to create.
1904 :param crypto_alg: The encryption algorithm name.
1905 :param integ_alg: The integrity algorithm name.
1906 :param tunnel_ip1: Tunnel node1 IPv4 address.
1907 :param tunnel_ip2: Tunnel node2 IPv4 address.
1908 :param raddr_ip1: Policy selector remote IPv4 start address for the
1909 first tunnel in direction node1->node2.
1910 :param raddr_ip2: Policy selector remote IPv4 start address for the
1911 first tunnel in direction node2->node1.
1912 :param raddr_range: Mask specifying range of Policy selector Remote
1913 IPv4 addresses. Valid values are from 1 to 32.
1915 :type interface1: str or int
1916 :type interface2: str or int
1917 :type n_tunnels: int
1918 :type crypto_alg: CryptoAlg
1919 :type integ_alg: Optional[IntegAlg]
1920 :type tunnel_ip1: str
1921 :type tunnel_ip2: str
1922 :type raddr_ip1: string
1923 :type raddr_ip2: string
1924 :type raddr_range: int
1934 crypto_key = gen_key(
1935 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1937 integ_key = gen_key(
1938 IPsecUtil.get_integ_alg_key_len(integ_alg)
1939 ).decode() if integ_alg else u""
1941 IPsecUtil.vpp_ipsec_set_ip_route(
1942 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1943 interface1, raddr_range)
1944 IPsecUtil.vpp_ipsec_set_ip_route(
1945 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1946 interface2, raddr_range)
1948 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1949 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1950 IPsecUtil.vpp_ipsec_policy_add(
1951 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1952 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1954 IPsecUtil.vpp_ipsec_policy_add(
1955 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1956 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1959 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1960 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1961 IPsecUtil.vpp_ipsec_policy_add(
1962 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1963 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1965 IPsecUtil.vpp_ipsec_policy_add(
1966 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1967 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1970 IPsecUtil.vpp_ipsec_add_sad_entries(
1971 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1972 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1974 IPsecUtil.vpp_ipsec_spd_add_entries(
1975 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1978 IPsecUtil.vpp_ipsec_add_sad_entries(
1979 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1980 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1982 IPsecUtil.vpp_ipsec_spd_add_entries(
1983 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1986 IPsecUtil.vpp_ipsec_add_sad_entries(
1987 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1988 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1991 IPsecUtil.vpp_ipsec_spd_add_entries(
1992 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1995 IPsecUtil.vpp_ipsec_add_sad_entries(
1996 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1997 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2000 IPsecUtil.vpp_ipsec_spd_add_entries(
2001 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
2005 def vpp_ipsec_show(node):
2006 """Run "show ipsec" debug CLI command.
2008 :param node: Node to run command on.
2011 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
2014 def show_ipsec_security_association(node):
2015 """Show IPSec security association.
2017 :param node: DUT node.
2023 PapiSocketExecutor.dump_and_log(node, cmds)