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,
556 raddr_range, dst_mac=None):
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 conf = f"{conf}exec set ip neighbor {if_name} " \
595 f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
598 VatExecutor().execute_script(
599 tmp_filename, node, timeout=300, json_out=False,
602 os.remove(tmp_filename)
605 cmd1 = u"sw_interface_add_del_address"
607 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
612 cmd2 = u"ip_route_add_del"
618 cmd3 = u"ip_neighbor_add_del"
622 sw_if_index=Topology.get_interface_sw_index(node, interface),
624 mac_address=str(dst_mac),
628 err_msg = f"Failed to configure IP addresses, IP routes and " \
629 f"IP neighbor on interface {interface} on host {node[u'host']}" \
631 else f"Failed to configure IP addresses and IP routes " \
632 f"on interface {interface} on host {node[u'host']}"
634 with PapiSocketExecutor(node) as papi_exec:
635 for i in range(n_tunnels):
636 args1[u"prefix"] = IPUtil.create_prefix_object(
637 tunnel_src + i * addr_incr, raddr_range
639 args2[u"route"] = IPUtil.compose_vpp_route_structure(
640 node, traffic_addr + i,
641 prefix_len=128 if traffic_addr.version == 6 else 32,
642 interface=interface, gateway=tunnel_dst + i * addr_incr
644 history = bool(not 1 < i < n_tunnels - 2)
645 papi_exec.add(cmd1, history=history, **args1).\
646 add(cmd2, history=history, **args2)
648 args3[u"neighbor"][u"ip_address"] = ip_address(
649 tunnel_dst + i * addr_incr
651 papi_exec.add(cmd3, history=history, **args3)
652 papi_exec.get_replies(err_msg)
655 def vpp_ipsec_add_spd(node, spd_id):
656 """Create Security Policy Database on the VPP node.
658 :param node: VPP node to add SPD on.
659 :param spd_id: SPD ID.
663 cmd = u"ipsec_spd_add_del"
664 err_msg = f"Failed to add Security Policy Database " \
665 f"on host {node[u'host']}"
670 with PapiSocketExecutor(node) as papi_exec:
671 papi_exec.add(cmd, **args).get_reply(err_msg)
674 def vpp_ipsec_spd_add_if(node, spd_id, interface):
675 """Add interface to the Security Policy Database.
677 :param node: VPP node.
678 :param spd_id: SPD ID to add interface on.
679 :param interface: Interface name or sw_if_index.
682 :type interface: str or int
684 cmd = u"ipsec_interface_add_del_spd"
685 err_msg = f"Failed to add interface {interface} to Security Policy " \
686 f"Database {spd_id} on host {node[u'host']}"
689 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
692 with PapiSocketExecutor(node) as papi_exec:
693 papi_exec.add(cmd, **args).get_reply(err_msg)
696 def vpp_ipsec_policy_add(
697 node, spd_id, priority, action, inbound=True, sa_id=None,
698 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
699 rport_range=None, is_ipv6=False):
700 """Create Security Policy Database entry on the VPP node.
702 :param node: VPP node to add SPD entry on.
703 :param spd_id: SPD ID to add entry on.
704 :param priority: SPD entry priority, higher number = higher priority.
705 :param action: Policy action.
706 :param inbound: If True policy is for inbound traffic, otherwise
708 :param sa_id: SAD entry ID for protect action.
709 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
710 format IP/prefix or IP/mask. If no mask is provided,
711 it's considered to be /32.
712 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
713 format IP/prefix or IP/mask. If no mask is provided,
714 it's considered to be /32.
715 :param proto: Policy selector next layer protocol number.
716 :param lport_range: Policy selector local TCP/UDP port range in format
717 <port_start>-<port_end>.
718 :param rport_range: Policy selector remote TCP/UDP port range in format
719 <port_start>-<port_end>.
720 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
721 not defined so it will default to address ::/0, otherwise False.
725 :type action: PolicyAction
728 :type laddr_range: string
729 :type raddr_range: string
731 :type lport_range: string
732 :type rport_range: string
735 if laddr_range is None:
736 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
738 if raddr_range is None:
739 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
741 cmd = u"ipsec_spd_entry_add_del"
742 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
743 f"on host {node[u'host']}"
747 priority=int(priority),
748 is_outbound=not inbound,
749 sa_id=int(sa_id) if sa_id else 0,
750 policy=action.policy_int_repr,
751 protocol=int(proto) if proto else 0,
752 remote_address_start=IPAddress.create_ip_address_object(
753 ip_network(raddr_range, strict=False).network_address
755 remote_address_stop=IPAddress.create_ip_address_object(
756 ip_network(raddr_range, strict=False).broadcast_address
758 local_address_start=IPAddress.create_ip_address_object(
759 ip_network(laddr_range, strict=False).network_address
761 local_address_stop=IPAddress.create_ip_address_object(
762 ip_network(laddr_range, strict=False).broadcast_address
764 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
766 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
768 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
770 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
777 with PapiSocketExecutor(node) as papi_exec:
778 papi_exec.add(cmd, **args).get_reply(err_msg)
781 def vpp_ipsec_spd_add_entries(
782 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
784 """Create multiple Security Policy Database entries on the VPP node.
786 :param node: VPP node to add SPD entries on.
787 :param n_entries: Number of SPD entries to be added.
788 :param spd_id: SPD ID to add entries on.
789 :param priority: SPD entries priority, higher number = higher priority.
790 :param inbound: If True policy is for inbound traffic, otherwise
792 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
793 SAD entry ID incremented by 1.
794 :param raddr_ip: Policy selector remote IPv4 start address for the first
795 entry. Remote IPv4 end address will be calculated depending on
796 raddr_range parameter. Each subsequent entry will have start address
797 next after IPv4 end address of previous entry.
798 :param raddr_range: Required IP addres range.
806 :type raddr_range: int
808 raddr_ip = ip_address(raddr_ip)
809 if int(n_entries) > 10:
810 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
812 with open(tmp_filename, 'w') as tmp_file:
813 for i in range(n_entries):
814 direction = u'inbound' if inbound else u'outbound'
815 tunnel = f"exec ipsec policy add spd {spd_id} " \
816 f"priority {priority} {direction} " \
817 f"action protect sa {sa_id+i} " \
818 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
819 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
820 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
821 tmp_file.write(tunnel)
822 VatExecutor().execute_script(
823 tmp_filename, node, timeout=300, json_out=False,
826 os.remove(tmp_filename)
829 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
831 cmd = u"ipsec_spd_entry_add_del"
832 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
833 f"on host {node[u'host']}"
837 priority=int(priority),
838 is_outbound=not inbound,
839 sa_id=int(sa_id) if sa_id else 0,
840 policy=getattr(PolicyAction.PROTECT, u"policy_int_repr"),
842 remote_address_start=IPAddress.create_ip_address_object(raddr_ip),
843 remote_address_stop=IPAddress.create_ip_address_object(raddr_ip),
844 local_address_start=IPAddress.create_ip_address_object(
845 ip_network(laddr_range, strict=False).network_address
847 local_address_stop=IPAddress.create_ip_address_object(
848 ip_network(laddr_range, strict=False).broadcast_address
851 remote_port_stop=65535,
853 local_port_stop=65535
860 with PapiSocketExecutor(node) as papi_exec:
861 for i in range(n_entries):
862 args[u"entry"][u"remote_address_start"][u"un"] = \
863 IPAddress.union_addr(raddr_ip + i)
864 args[u"entry"][u"remote_address_stop"][u"un"] = \
865 IPAddress.union_addr(raddr_ip + i)
866 history = bool(not 1 < i < n_entries - 2)
867 papi_exec.add(cmd, history=history, **args)
868 papi_exec.get_replies(err_msg)
871 def _ipsec_create_tunnel_interfaces_dut1_vat(
872 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
873 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
874 """Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
876 Generate random keys and return them (so DUT2 or TG can decrypt).
878 :param nodes: VPP nodes to create tunnel interfaces.
879 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
880 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
881 IPv4/IPv6 address (ip2).
882 :param if1_key: VPP node 1 interface key from topology file.
883 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
884 interface key from topology file.
885 :param n_tunnels: Number of tunnel interfaces to be there at the end.
886 :param crypto_alg: The encryption algorithm name.
887 :param integ_alg: The integrity algorithm name.
888 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
889 first tunnel in direction node2->node1.
890 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
891 :param addr_incr: IP / IPv6 address incremental step.
892 :param existing_tunnels: Number of tunnel interfaces before creation.
893 Useful mainly for reconf tests. Default 0.
899 :type crypto_alg: CryptoAlg
900 :type integ_alg: Optional[IntegAlg]
901 :type raddr_ip2: IPv4Address or IPv6Address
904 :type existing_tunnels: int
905 :returns: Generated ckeys and ikeys.
906 :rtype: List[bytes], List[bytes]
908 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
909 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
911 ckeys = [bytes()] * existing_tunnels
912 ikeys = [bytes()] * existing_tunnels
915 with open(tmp_fn1, u"w") as tmp_f1:
916 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key) \
917 if u"DUT2" in nodes.keys() \
918 else Topology.get_interface_mac(nodes[u"TG"], if2_key)
919 if not existing_tunnels:
921 f"exec create loopback interface\n"
922 f"exec set interface state loop0 up\n"
923 f"exec set interface ip address {if1_n} "
924 f"{tun_ips[u'ip2'] - 1}/"
925 f"{len(tun_ips[u'ip2'].packed)*8*3//4}\n"
926 f"exec set ip neighbor {if1_n} {tun_ips[u'ip2']} {rmac} "
929 for i in range(existing_tunnels, n_tunnels):
931 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
934 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
937 integ = f"integ-alg {integ_alg.alg_name} " \
938 f"integ-key {ikeys[i].hex()} "
942 f"exec set interface ip address loop0 "
943 f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
944 f"exec create ipip tunnel "
945 f"src {tun_ips[u'ip1'] + i * addr_incr} "
946 f"dst {tun_ips[u'ip2']} "
948 f"exec ipsec sa add {i} "
949 f"spi {spi_d[u'spi_1'] + i} "
950 f"crypto-alg {crypto_alg.alg_name} "
951 f"crypto-key {ckeys[i].hex()} "
954 f"exec ipsec sa add {100000 + i} "
955 f"spi {spi_d[u'spi_2'] + i} "
956 f"crypto-alg {crypto_alg.alg_name} "
957 f"crypto-key {ckeys[i].hex()} "
960 f"exec ipsec tunnel protect ipip{i} "
962 f"sa-in {100000 + i} "
966 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
967 copy_on_execute=True,
968 history=bool(n_tunnels < 100)
972 with open(tmp_fn1, 'w') as tmp_f1:
973 for i in range(existing_tunnels, n_tunnels):
975 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
976 f"exec set interface state ipip{i} up\n"
977 f"exec ip route add "
978 f"{raddr_ip2 + i}/{len(raddr_ip2.packed)*8} "
982 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
983 copy_on_execute=True,
984 history=bool(n_tunnels < 100)
991 def _ipsec_create_tunnel_interfaces_dut2_vat(
992 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
993 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
994 """Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
996 This method accesses keys generated by DUT1 method
997 and does not return anything.
999 :param nodes: VPP nodes to create tunnel interfaces.
1000 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1001 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1002 IPv4/IPv6 address (ip2).
1003 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1004 interface key from topology file.
1005 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1006 :param crypto_alg: The encryption algorithm name.
1007 :param ckeys: List of encryption keys.
1008 :param integ_alg: The integrity algorithm name.
1009 :param ikeys: List of integrity keys.
1010 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1011 :param addr_incr: IP / IPv6 address incremental step.
1012 :param existing_tunnels: Number of tunnel interfaces before creation.
1013 Useful mainly for reconf tests. Default 0.
1017 :type n_tunnels: int
1018 :type crypto_alg: CryptoAlg
1019 :type ckeys: Sequence[bytes]
1020 :type integ_alg: Optional[IntegAlg]
1021 :type ikeys: Sequence[bytes]
1022 :type addr_incr: int
1024 :type existing_tunnels: int
1026 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
1027 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
1030 with open(tmp_fn2, 'w') as tmp_f2:
1031 if not existing_tunnels:
1033 f"exec set interface ip address {if2_n}"
1034 f" {tun_ips[u'ip2']}/{len(tun_ips[u'ip2'].packed)*8*3/4}\n"
1036 for i in range(existing_tunnels, n_tunnels):
1038 integ = f"integ-alg {integ_alg.alg_name} " \
1039 f"integ-key {ikeys[i].hex()} "
1043 f"exec create ipip tunnel "
1044 f"src {tun_ips[u'ip2']} "
1045 f"dst {tun_ips[u'ip1'] + i * addr_incr} "
1047 f"exec ipsec sa add {100000 + i} "
1048 f"spi {spi_d[u'spi_2'] + i} "
1049 f"crypto-alg {crypto_alg.alg_name} "
1050 f"crypto-key {ckeys[i].hex()} "
1053 f"exec ipsec sa add {i} "
1054 f"spi {spi_d[u'spi_1'] + i} "
1055 f"crypto-alg {crypto_alg.alg_name} "
1056 f"crypto-key {ckeys[i].hex()} "
1059 f"exec ipsec tunnel protect ipip{i} "
1060 f"sa-out {100000 + i} "
1065 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1066 copy_on_execute=True,
1067 history=bool(n_tunnels < 100)
1071 with open(tmp_fn2, 'w') as tmp_f2:
1072 if not existing_tunnels:
1074 f"exec ip route add {tun_ips[u'ip1']}/8 "
1075 f"via {tun_ips[u'ip2'] - 1} {if2_n}\n"
1077 for i in range(existing_tunnels, n_tunnels):
1079 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
1080 f"exec set interface state ipip{i} up\n"
1081 f"exec ip route add "
1082 f"{raddr_ip1 + i}/{len(raddr_ip1.packed)*8} "
1086 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
1087 copy_on_execute=True,
1088 history=bool(n_tunnels < 100)
1093 def _ipsec_create_loopback_dut1_papi(nodes, tun_ips, if1_key, if2_key):
1094 """Create loopback interface and set IP address on VPP node 1 interface
1097 :param nodes: VPP nodes to create tunnel interfaces.
1098 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1099 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1100 IPv4/IPv6 address (ip2).
1101 :param if1_key: VPP node 1 interface key from topology file.
1102 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1103 interface key from topology file.
1109 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1110 # Create loopback interface on DUT1, set it to up state
1111 cmd = u"create_loopback_instance"
1117 err_msg = f"Failed to create loopback interface " \
1118 f"on host {nodes[u'DUT1'][u'host']}"
1119 loop_sw_if_idx = papi_exec.add(cmd, **args). \
1120 get_sw_if_index(err_msg)
1121 cmd = u"sw_interface_set_flags"
1123 sw_if_index=loop_sw_if_idx,
1124 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1126 err_msg = f"Failed to set loopback interface state up " \
1127 f"on host {nodes[u'DUT1'][u'host']}"
1128 papi_exec.add(cmd, **args).get_reply(err_msg)
1129 # Set IP address on VPP node 1 interface
1130 cmd = u"sw_interface_add_del_address"
1132 sw_if_index=InterfaceUtil.get_interface_index(
1133 nodes[u"DUT1"], if1_key
1137 prefix=IPUtil.create_prefix_object(
1138 tun_ips[u"ip2"] - 1, 96 if tun_ips[u"ip2"].version == 6
1142 err_msg = f"Failed to set IP address on interface {if1_key} " \
1143 f"on host {nodes[u'DUT1'][u'host']}"
1144 papi_exec.add(cmd, **args).get_reply(err_msg)
1145 cmd2 = u"ip_neighbor_add_del"
1149 sw_if_index=Topology.get_interface_sw_index(
1150 nodes[u"DUT1"], if1_key
1154 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
1155 if u"DUT2" in nodes.keys()
1156 else Topology.get_interface_mac(
1157 nodes[u"TG"], if2_key
1160 ip_address=tun_ips[u"ip2"].compressed
1163 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
1164 papi_exec.add(cmd2, **args2).get_reply(err_msg)
1166 return loop_sw_if_idx
1169 def _ipsec_create_tunnel_interfaces_dut1_papi(
1170 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg,
1171 raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
1172 """Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
1174 Generate random keys and return them (so DUT2 or TG can decrypt).
1176 :param nodes: VPP nodes to create tunnel interfaces.
1177 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1178 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1179 IPv4/IPv6 address (ip2).
1180 :param if1_key: VPP node 1 interface key from topology file.
1181 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1182 interface key from topology file.
1183 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1184 :param crypto_alg: The encryption algorithm name.
1185 :param integ_alg: The integrity algorithm name.
1186 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1187 first tunnel in direction node2->node1.
1188 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1189 :param addr_incr: IP / IPv6 address incremental step.
1190 :param existing_tunnels: Number of tunnel interfaces before creation.
1191 Useful mainly for reconf tests. Default 0.
1196 :type n_tunnels: int
1197 :type crypto_alg: CryptoAlg
1198 :type integ_alg: Optional[IntegAlg]
1199 :type raddr_ip2: IPv4Address or IPv6Address
1200 :type addr_incr: int
1202 :type existing_tunnels: int
1203 :returns: Generated ckeys and ikeys.
1204 :rtype: List[bytes], List[bytes]
1206 if not existing_tunnels:
1207 loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
1208 nodes, tun_ips, if1_key, if2_key
1211 loop_sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(
1212 nodes[u"DUT1"], u"loop0"
1214 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
1215 # Configure IP addresses on loop0 interface
1216 cmd = u"sw_interface_add_del_address"
1218 sw_if_index=loop_sw_if_idx,
1223 for i in range(existing_tunnels, n_tunnels):
1224 args[u"prefix"] = IPUtil.create_prefix_object(
1225 tun_ips[u"ip1"] + i * addr_incr,
1226 128 if tun_ips[u"ip1"].version == 6 else 32
1229 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1231 # Configure IPIP tunnel interfaces
1232 cmd = u"ipip_add_tunnel"
1234 instance=Constants.BITWISE_NON_ZERO,
1239 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1241 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1242 dscp=int(IpDscp.IP_API_DSCP_CS0)
1247 ipip_tunnels = [None] * existing_tunnels
1248 for i in range(existing_tunnels, n_tunnels):
1249 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1250 tun_ips[u"ip1"] + i * addr_incr
1252 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1256 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1258 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1259 f" {nodes[u'DUT1'][u'host']}"
1260 ipip_tunnels.extend(
1262 reply[u"sw_if_index"]
1263 for reply in papi_exec.get_replies(err_msg)
1264 if u"sw_if_index" in reply
1267 # Configure IPSec SAD entries
1268 ckeys = [bytes()] * existing_tunnels
1269 ikeys = [bytes()] * existing_tunnels
1270 cmd = u"ipsec_sad_entry_add_del_v2"
1282 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1283 crypto_algorithm=crypto_alg.alg_int_repr,
1285 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1286 integrity_key=i_key,
1291 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1293 dscp=int(IpDscp.IP_API_DSCP_CS0),
1296 udp_src_port=IPSEC_UDP_PORT_NONE,
1297 udp_dst_port=IPSEC_UDP_PORT_NONE
1303 for i in range(existing_tunnels, n_tunnels):
1305 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1308 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1310 # SAD entry for outband / tx path
1311 args[u"entry"][u"sad_id"] = i
1312 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1314 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1315 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1317 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1318 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1319 args[u"entry"][u"flags"] = int(
1320 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1323 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1325 # SAD entry for inband / rx path
1326 args[u"entry"][u"sad_id"] = 100000 + i
1327 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1329 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1330 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1332 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1333 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1334 args[u"entry"][u"flags"] = int(
1335 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1336 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1339 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1341 err_msg = f"Failed to add IPsec SAD entries on host" \
1342 f" {nodes[u'DUT1'][u'host']}"
1343 papi_exec.get_replies(err_msg)
1344 # Add protection for tunnels with IPSEC
1345 cmd = u"ipsec_tunnel_protect_update"
1348 via_label=MPLS_LABEL_INVALID,
1349 obj_id=Constants.BITWISE_NON_ZERO
1351 ipsec_tunnel_protect = dict(
1359 tunnel=ipsec_tunnel_protect
1361 for i in range(existing_tunnels, n_tunnels):
1362 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1363 args[u"tunnel"][u"sa_out"] = i
1364 args[u"tunnel"][u"sa_in"] = [100000 + i]
1366 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1368 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1369 f"on host {nodes[u'DUT1'][u'host']}"
1370 papi_exec.get_replies(err_msg)
1372 # Configure unnumbered interfaces
1373 cmd = u"sw_interface_set_unnumbered"
1376 sw_if_index=InterfaceUtil.get_interface_index(
1377 nodes[u"DUT1"], if1_key
1379 unnumbered_sw_if_index=0
1381 for i in range(existing_tunnels, n_tunnels):
1382 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1384 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1387 cmd = u"sw_interface_set_flags"
1390 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1392 for i in range(existing_tunnels, n_tunnels):
1393 args[u"sw_if_index"] = ipip_tunnels[i]
1395 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1397 # Configure IP routes
1398 cmd = u"ip_route_add_del"
1404 for i in range(existing_tunnels, n_tunnels):
1405 args[u"route"] = IPUtil.compose_vpp_route_structure(
1406 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1407 prefix_len=128 if raddr_ip2.version == 6 else 32,
1408 interface=ipip_tunnels[i]
1411 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1413 err_msg = f"Failed to add IP routes on host " \
1414 f"{nodes[u'DUT1'][u'host']}"
1415 papi_exec.get_replies(err_msg)
1420 def _ipsec_create_tunnel_interfaces_dut2_papi(
1421 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys, integ_alg,
1422 ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
1423 """Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
1425 This method accesses keys generated by DUT1 method
1426 and does not return anything.
1428 :param nodes: VPP nodes to create tunnel interfaces.
1429 :param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
1430 IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
1431 IPv4/IPv6 address (ip2).
1432 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1433 interface key from topology file.
1434 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1435 :param crypto_alg: The encryption algorithm name.
1436 :param ckeys: List of encryption keys.
1437 :param integ_alg: The integrity algorithm name.
1438 :param ikeys: List of integrity keys.
1439 :param spi_d: Dictionary with SPIs for VPP node 1 and VPP node 2.
1440 :param addr_incr: IP / IPv6 address incremental step.
1441 :param existing_tunnels: Number of tunnel interfaces before creation.
1442 Useful mainly for reconf tests. Default 0.
1446 :type n_tunnels: int
1447 :type crypto_alg: CryptoAlg
1448 :type ckeys: Sequence[bytes]
1449 :type integ_alg: Optional[IntegAlg]
1450 :type ikeys: Sequence[bytes]
1451 :type addr_incr: int
1453 :type existing_tunnels: int
1455 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1456 if not existing_tunnels:
1457 # Set IP address on VPP node 2 interface
1458 cmd = u"sw_interface_add_del_address"
1460 sw_if_index=InterfaceUtil.get_interface_index(
1461 nodes[u"DUT2"], if2_key
1465 prefix=IPUtil.create_prefix_object(
1466 tun_ips[u"ip2"], 96 if tun_ips[u"ip2"].version == 6
1470 err_msg = f"Failed to set IP address on interface {if2_key} " \
1471 f"on host {nodes[u'DUT2'][u'host']}"
1472 papi_exec.add(cmd, **args).get_reply(err_msg)
1473 # Configure IPIP tunnel interfaces
1474 cmd = u"ipip_add_tunnel"
1476 instance=Constants.BITWISE_NON_ZERO,
1481 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1483 mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
1484 dscp=int(IpDscp.IP_API_DSCP_CS0)
1489 ipip_tunnels = [None] * existing_tunnels
1490 for i in range(existing_tunnels, n_tunnels):
1491 args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
1494 args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
1495 tun_ips[u"ip1"] + i * addr_incr
1498 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1500 err_msg = f"Failed to add IPIP tunnel interfaces on host" \
1501 f" {nodes[u'DUT2'][u'host']}"
1502 ipip_tunnels.extend(
1504 reply[u"sw_if_index"]
1505 for reply in papi_exec.get_replies(err_msg)
1506 if u"sw_if_index" in reply
1509 # Configure IPSec SAD entries
1510 cmd = u"ipsec_sad_entry_add_del_v2"
1522 protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
1524 crypto_algorithm=crypto_alg.alg_int_repr,
1526 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
1527 integrity_key=i_key,
1533 TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
1535 dscp=int(IpDscp.IP_API_DSCP_CS0),
1538 udp_src_port=IPSEC_UDP_PORT_NONE,
1539 udp_dst_port=IPSEC_UDP_PORT_NONE
1545 for i in range(existing_tunnels, n_tunnels):
1547 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
1550 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
1552 # SAD entry for outband / tx path
1553 args[u"entry"][u"sad_id"] = 100000 + i
1554 args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
1556 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1557 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1559 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1560 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1561 args[u"entry"][u"flags"] = int(
1562 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
1565 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1567 # SAD entry for inband / rx path
1568 args[u"entry"][u"sad_id"] = i
1569 args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
1571 args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
1572 args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
1574 args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
1575 args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
1576 args[u"entry"][u"flags"] = int(
1577 IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
1578 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
1581 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1583 err_msg = f"Failed to add IPsec SAD entries on host" \
1584 f" {nodes[u'DUT2'][u'host']}"
1585 papi_exec.get_replies(err_msg)
1586 # Add protection for tunnels with IPSEC
1587 cmd = u"ipsec_tunnel_protect_update"
1590 via_label=MPLS_LABEL_INVALID,
1591 obj_id=Constants.BITWISE_NON_ZERO
1593 ipsec_tunnel_protect = dict(
1601 tunnel=ipsec_tunnel_protect
1603 for i in range(existing_tunnels, n_tunnels):
1604 args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
1605 args[u"tunnel"][u"sa_out"] = 100000 + i
1606 args[u"tunnel"][u"sa_in"] = [i]
1608 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1610 err_msg = f"Failed to add protection for tunnels with IPSEC " \
1611 f"on host {nodes[u'DUT2'][u'host']}"
1612 papi_exec.get_replies(err_msg)
1614 if not existing_tunnels:
1615 # Configure IP route
1616 cmd = u"ip_route_add_del"
1617 route = IPUtil.compose_vpp_route_structure(
1618 nodes[u"DUT2"], tun_ips[u"ip1"].compressed,
1619 prefix_len=32 if tun_ips[u"ip1"].version == 6 else 8,
1621 gateway=(tun_ips[u"ip2"] - 1).compressed
1628 papi_exec.add(cmd, **args)
1629 # Configure unnumbered interfaces
1630 cmd = u"sw_interface_set_unnumbered"
1633 sw_if_index=InterfaceUtil.get_interface_index(
1634 nodes[u"DUT2"], if2_key
1636 unnumbered_sw_if_index=0
1638 for i in range(existing_tunnels, n_tunnels):
1639 args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
1641 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1644 cmd = u"sw_interface_set_flags"
1647 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1649 for i in range(existing_tunnels, n_tunnels):
1650 args[u"sw_if_index"] = ipip_tunnels[i]
1652 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1654 # Configure IP routes
1655 cmd = u"ip_route_add_del"
1661 for i in range(existing_tunnels, n_tunnels):
1662 args[u"route"] = IPUtil.compose_vpp_route_structure(
1663 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1664 prefix_len=128 if raddr_ip1.version == 6 else 32,
1665 interface=ipip_tunnels[i]
1668 cmd, history=bool(not 1 < i < n_tunnels - 2), **args
1670 err_msg = f"Failed to add IP routes " \
1671 f"on host {nodes[u'DUT2'][u'host']}"
1672 papi_exec.get_replies(err_msg)
1675 def vpp_ipsec_create_tunnel_interfaces(
1676 nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
1677 n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
1678 existing_tunnels=0, return_keys=False):
1679 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1681 Some deployments (e.g. devicetest) need to know the generated keys.
1682 But other deployments (e.g. scale perf test) would get spammed
1683 if we returned keys every time.
1685 :param nodes: VPP nodes to create tunnel interfaces.
1686 :param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
1688 :param tun_if2_ip_addr: VPP node 2 ipsec tunnel interface IPv4/IPv6
1690 :param if1_key: VPP node 1 interface key from topology file.
1691 :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
1692 interface key from topology file.
1693 :param n_tunnels: Number of tunnel interfaces to be there at the end.
1694 :param crypto_alg: The encryption algorithm name.
1695 :param integ_alg: The integrity algorithm name.
1696 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
1697 first tunnel in direction node1->node2.
1698 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
1699 first tunnel in direction node2->node1.
1700 :param raddr_range: Mask specifying range of Policy selector Remote
1701 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
1702 and to 128 in case of IPv6.
1703 :param existing_tunnels: Number of tunnel interfaces before creation.
1704 Useful mainly for reconf tests. Default 0.
1705 :param return_keys: Whether generated keys should be returned.
1707 :type tun_if1_ip_addr: str
1708 :type tun_if2_ip_addr: str
1711 :type n_tunnels: int
1712 :type crypto_alg: CryptoAlg
1713 :type integ_alg: Optonal[IntegAlg]
1714 :type raddr_ip1: string
1715 :type raddr_ip2: string
1716 :type raddr_range: int
1717 :type existing_tunnels: int
1718 :type return_keys: bool
1719 :returns: Ckeys, ikeys, spi_1, spi_2.
1720 :rtype: Optional[List[bytes], List[bytes], int, int]
1722 n_tunnels = int(n_tunnels)
1723 existing_tunnels = int(existing_tunnels)
1729 ip1=ip_address(tun_if1_ip_addr),
1730 ip2=ip_address(tun_if2_ip_addr)
1732 raddr_ip1 = ip_address(raddr_ip1)
1733 raddr_ip2 = ip_address(raddr_ip2)
1734 addr_incr = 1 << (128 - raddr_range) if tun_ips[u"ip1"].version == 6 \
1735 else 1 << (32 - raddr_range)
1737 if n_tunnels - existing_tunnels > 10:
1738 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_vat(
1739 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1740 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1742 if u"DUT2" in nodes.keys():
1743 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
1744 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1745 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1749 ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
1750 nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
1751 integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
1753 if u"DUT2" in nodes.keys():
1754 IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
1755 nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
1756 integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
1761 return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
1765 def _create_ipsec_script_files(dut, instances):
1766 """Create script files for configuring IPsec in containers
1768 :param dut: DUT node on which to create the script files
1769 :param instances: number of containers on DUT node
1771 :type instances: int
1774 for cnf in range(0, instances):
1776 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1778 scripts.append(open(script_filename, 'w'))
1782 def _close_and_copy_ipsec_script_files(
1783 dut, nodes, instances, scripts):
1784 """Close created scripts and copy them to containers
1786 :param dut: DUT node on which to create the script files
1787 :param nodes: VPP nodes
1788 :param instances: number of containers on DUT node
1789 :param scripts: dictionary holding the script files
1792 :type instances: int
1795 for cnf in range(0, instances):
1796 scripts[cnf].close()
1798 f"/tmp/ipsec_create_tunnel_cnf_{dut}_{cnf + 1}.config"
1800 scp_node(nodes[dut], script_filename, script_filename)
1804 def vpp_ipsec_create_tunnel_interfaces_in_containers(
1805 nodes, if1_ip_addr, if2_ip_addr, n_tunnels, crypto_alg, integ_alg,
1806 raddr_ip1, raddr_ip2, raddr_range, n_instances):
1807 """Create multiple IPsec tunnel interfaces between two VPP nodes.
1809 :param nodes: VPP nodes to create tunnel interfaces.
1810 :param if1_ip_addr: VPP node 1 interface IP4 address.
1811 :param if2_ip_addr: VPP node 2 interface IP4 address.
1812 :param n_tunnels: Number of tunnell interfaces to create.
1813 :param crypto_alg: The encryption algorithm name.
1814 :param integ_alg: The integrity algorithm name.
1815 :param raddr_ip1: Policy selector remote IPv4 start address for the
1816 first tunnel in direction node1->node2.
1817 :param raddr_ip2: Policy selector remote IPv4 start address for the
1818 first tunnel in direction node2->node1.
1819 :param raddr_range: Mask specifying range of Policy selector Remote
1820 IPv4 addresses. Valid values are from 1 to 32.
1821 :param n_instances: Number of containers.
1823 :type if1_ip_addr: str
1824 :type if2_ip_addr: str
1825 :type n_tunnels: int
1826 :type crypto_alg: CryptoAlg
1827 :type integ_alg: Optional[IntegAlg]
1828 :type raddr_ip1: string
1829 :type raddr_ip2: string
1830 :type raddr_range: int
1831 :type n_instances: int
1835 addr_incr = 1 << (32 - raddr_range)
1837 dut1_scripts = IPsecUtil._create_ipsec_script_files(
1838 u"DUT1", n_instances
1840 dut2_scripts = IPsecUtil._create_ipsec_script_files(
1841 u"DUT2", n_instances
1844 for cnf in range(0, n_instances):
1845 dut1_scripts[cnf].write(
1846 u"create loopback interface\n"
1847 u"set interface state loop0 up\n\n"
1849 dut2_scripts[cnf].write(
1850 f"ip route add {if1_ip_addr}/8 via "
1851 f"{ip_address(if2_ip_addr) + cnf + 100} memif1/{cnf + 1}\n\n"
1854 for tnl in range(0, n_tunnels):
1855 cnf = tnl % n_instances
1857 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
1861 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
1865 f"integ-alg {integ_alg.alg_name} "
1866 f"local-integ-key {ikey} "
1867 f"remote-integ-key {ikey} "
1869 # Configure tunnel end point(s) on left side
1870 dut1_scripts[cnf].write(
1871 u"set interface ip address loop0 "
1872 f"{ip_address(if1_ip_addr) + tnl * addr_incr}/32\n"
1873 f"create ipsec tunnel "
1874 f"local-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1875 f"local-spi {spi_1 + tnl} "
1876 f"remote-ip {ip_address(if2_ip_addr) + cnf} "
1877 f"remote-spi {spi_2 + tnl} "
1878 f"crypto-alg {crypto_alg.alg_name} "
1879 f"local-crypto-key {ckey} "
1880 f"remote-crypto-key {ckey} "
1881 f"instance {tnl // n_instances} "
1884 f"set interface unnumbered ipip{tnl // n_instances} use loop0\n"
1885 f"set interface state ipip{tnl // n_instances} up\n"
1886 f"ip route add {ip_address(raddr_ip2)+tnl}/32 "
1887 f"via ipip{tnl // n_instances}\n\n"
1889 # Configure tunnel end point(s) on right side
1890 dut2_scripts[cnf].write(
1891 f"set ip neighbor memif1/{cnf + 1} "
1892 f"{ip_address(if1_ip_addr) + tnl * addr_incr} "
1893 f"02:02:00:00:{17:02X}:{cnf:02X} static\n"
1894 f"create ipsec tunnel local-ip {ip_address(if2_ip_addr) + cnf} "
1895 f"local-spi {spi_2 + tnl} "
1896 f"remote-ip {ip_address(if1_ip_addr) + tnl * addr_incr} "
1897 f"remote-spi {spi_1 + tnl} "
1898 f"crypto-alg {crypto_alg.alg_name} "
1899 f"local-crypto-key {ckey} "
1900 f"remote-crypto-key {ckey} "
1901 f"instance {tnl // n_instances} "
1904 f"set interface unnumbered ipip{tnl // n_instances} "
1905 f"use memif1/{cnf + 1}\n"
1906 f"set interface state ipip{tnl // n_instances} up\n"
1907 f"ip route add {ip_address(raddr_ip1) + tnl}/32 "
1908 f"via ipip{tnl // n_instances}\n\n"
1911 IPsecUtil._close_and_copy_ipsec_script_files(
1912 u"DUT1", nodes, n_instances, dut1_scripts)
1913 IPsecUtil._close_and_copy_ipsec_script_files(
1914 u"DUT2", nodes, n_instances, dut2_scripts)
1917 def vpp_ipsec_add_multiple_tunnels(
1918 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1919 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1920 """Create multiple IPsec tunnels between two VPP nodes.
1922 :param nodes: VPP nodes to create tunnels.
1923 :param interface1: Interface name or sw_if_index on node 1.
1924 :param interface2: Interface name or sw_if_index on node 2.
1925 :param n_tunnels: Number of tunnels to create.
1926 :param crypto_alg: The encryption algorithm name.
1927 :param integ_alg: The integrity algorithm name.
1928 :param tunnel_ip1: Tunnel node1 IPv4 address.
1929 :param tunnel_ip2: Tunnel node2 IPv4 address.
1930 :param raddr_ip1: Policy selector remote IPv4 start address for the
1931 first tunnel in direction node1->node2.
1932 :param raddr_ip2: Policy selector remote IPv4 start address for the
1933 first tunnel in direction node2->node1.
1934 :param raddr_range: Mask specifying range of Policy selector Remote
1935 IPv4 addresses. Valid values are from 1 to 32.
1937 :type interface1: str or int
1938 :type interface2: str or int
1939 :type n_tunnels: int
1940 :type crypto_alg: CryptoAlg
1941 :type integ_alg: Optional[IntegAlg]
1942 :type tunnel_ip1: str
1943 :type tunnel_ip2: str
1944 :type raddr_ip1: string
1945 :type raddr_ip2: string
1946 :type raddr_range: int
1956 crypto_key = gen_key(
1957 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1959 integ_key = gen_key(
1960 IPsecUtil.get_integ_alg_key_len(integ_alg)
1961 ).decode() if integ_alg else u""
1963 rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
1964 if u"DUT2" in nodes.keys() \
1965 else Topology.get_interface_mac(nodes[u"TG"], interface2)
1966 IPsecUtil.vpp_ipsec_set_ip_route(
1967 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1968 interface1, raddr_range, rmac)
1970 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1971 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1972 IPsecUtil.vpp_ipsec_policy_add(
1973 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1974 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1976 IPsecUtil.vpp_ipsec_policy_add(
1977 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1978 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1981 IPsecUtil.vpp_ipsec_add_sad_entries(
1982 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1983 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1985 IPsecUtil.vpp_ipsec_spd_add_entries(
1986 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1989 IPsecUtil.vpp_ipsec_add_sad_entries(
1990 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1991 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1993 IPsecUtil.vpp_ipsec_spd_add_entries(
1994 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1997 if u"DUT2" in nodes.keys():
1998 IPsecUtil.vpp_ipsec_set_ip_route(
1999 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
2000 interface2, raddr_range)
2002 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
2003 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
2004 IPsecUtil.vpp_ipsec_policy_add(
2005 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2006 inbound=False, proto=50, laddr_range=u"100.0.0.0/8",
2007 raddr_range=u"100.0.0.0/8"
2009 IPsecUtil.vpp_ipsec_policy_add(
2010 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
2011 inbound=True, proto=50, laddr_range=u"100.0.0.0/8",
2012 raddr_range=u"100.0.0.0/8"
2015 IPsecUtil.vpp_ipsec_add_sad_entries(
2016 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
2017 crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2
2019 IPsecUtil.vpp_ipsec_spd_add_entries(
2020 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1,
2024 IPsecUtil.vpp_ipsec_add_sad_entries(
2025 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
2026 crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1
2028 IPsecUtil.vpp_ipsec_spd_add_entries(
2029 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2,
2035 def vpp_ipsec_show(node):
2036 """Run "show ipsec" debug CLI command.
2038 :param node: Node to run command on.
2041 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
2044 def show_ipsec_security_association(node):
2045 """Show IPSec security association.
2047 :param node: DUT node.
2053 PapiSocketExecutor.dump_and_log(node, cmds)