1 # Copyright (c) 2019 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.IPUtil import IPUtil
26 from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
28 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
29 from resources.libraries.python.topology import Topology
30 from resources.libraries.python.VatExecutor import VatExecutor
34 """Generate random string as a key.
36 :param length: Length of generated payload.
38 :returns: The generated payload.
42 choice(ascii_letters) for _ in range(length)
43 ).encode(encoding=u"utf-8")
46 class PolicyAction(Enum):
48 BYPASS = (u"bypass", 0)
49 DISCARD = (u"discard", 1)
50 PROTECT = (u"protect", 3)
52 def __init__(self, policy_name, policy_int_repr):
53 self.policy_name = policy_name
54 self.policy_int_repr = policy_int_repr
57 class CryptoAlg(Enum):
58 """Encryption algorithms."""
59 AES_CBC_128 = (u"aes-cbc-128", 1, u"AES-CBC", 16)
60 AES_CBC_256 = (u"aes-cbc-256", 3, u"AES-CBC", 32)
61 AES_GCM_128 = (u"aes-gcm-128", 7, u"AES-GCM", 16)
62 AES_GCM_256 = (u"aes-gcm-256", 9, u"AES-GCM", 32)
64 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
65 self.alg_name = alg_name
66 self.alg_int_repr = alg_int_repr
67 self.scapy_name = scapy_name
68 self.key_len = key_len
72 """Integrity algorithm."""
73 SHA_256_128 = (u"sha-256-128", 4, u"SHA2-256-128", 32)
74 SHA_512_256 = (u"sha-512-256", 6, u"SHA2-512-256", 64)
76 def __init__(self, alg_name, alg_int_repr, scapy_name, key_len):
77 self.alg_name = alg_name
78 self.alg_int_repr = alg_int_repr
79 self.scapy_name = scapy_name
80 self.key_len = key_len
83 class IPsecProto(IntEnum):
89 class IPsecSadFlags(IntEnum):
90 """IPsec Security Association Database flags."""
91 IPSEC_API_SAD_FLAG_NONE = 0
92 IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
93 IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
97 """IPsec utilities."""
100 def policy_action_bypass():
101 """Return policy action bypass.
103 :returns: PolicyAction enum BYPASS object.
106 return PolicyAction.BYPASS
109 def policy_action_discard():
110 """Return policy action discard.
112 :returns: PolicyAction enum DISCARD object.
115 return PolicyAction.DISCARD
118 def policy_action_protect():
119 """Return policy action protect.
121 :returns: PolicyAction enum PROTECT object.
124 return PolicyAction.PROTECT
127 def crypto_alg_aes_cbc_128():
128 """Return encryption algorithm aes-cbc-128.
130 :returns: CryptoAlg enum AES_CBC_128 object.
133 return CryptoAlg.AES_CBC_128
136 def crypto_alg_aes_cbc_256():
137 """Return encryption algorithm aes-cbc-256.
139 :returns: CryptoAlg enum AES_CBC_256 object.
142 return CryptoAlg.AES_CBC_256
145 def crypto_alg_aes_gcm_128():
146 """Return encryption algorithm aes-gcm-128.
148 :returns: CryptoAlg enum AES_GCM_128 object.
151 return CryptoAlg.AES_GCM_128
154 def crypto_alg_aes_gcm_256():
155 """Return encryption algorithm aes-gcm-256.
157 :returns: CryptoAlg enum AES_GCM_128 object.
160 return CryptoAlg.AES_GCM_256
163 def get_crypto_alg_key_len(crypto_alg):
164 """Return encryption algorithm key length.
166 :param crypto_alg: Encryption algorithm.
167 :type crypto_alg: CryptoAlg
168 :returns: Key length.
171 return crypto_alg.key_len
174 def get_crypto_alg_scapy_name(crypto_alg):
175 """Return encryption algorithm scapy name.
177 :param crypto_alg: Encryption algorithm.
178 :type crypto_alg: CryptoAlg
179 :returns: Algorithm scapy name.
182 return crypto_alg.scapy_name
185 def integ_alg_sha_256_128():
186 """Return integrity algorithm SHA-256-128.
188 :returns: IntegAlg enum SHA_256_128 object.
191 return IntegAlg.SHA_256_128
194 def integ_alg_sha_512_256():
195 """Return integrity algorithm SHA-512-256.
197 :returns: IntegAlg enum SHA_512_256 object.
200 return IntegAlg.SHA_512_256
203 def get_integ_alg_key_len(integ_alg):
204 """Return integrity algorithm key length.
206 :param integ_alg: Integrity algorithm.
207 :type integ_alg: IntegAlg
208 :returns: Key length.
211 return integ_alg.key_len
214 def get_integ_alg_scapy_name(integ_alg):
215 """Return integrity algorithm scapy name.
217 :param integ_alg: Integrity algorithm.
218 :type integ_alg: IntegAlg
219 :returns: Algorithm scapy name.
222 return integ_alg.scapy_name
225 def ipsec_proto_esp():
226 """Return IPSec protocol ESP.
228 :returns: IPsecProto enum ESP object.
231 return int(IPsecProto.ESP)
234 def ipsec_proto_ah():
235 """Return IPSec protocol AH.
237 :returns: IPsecProto enum AH object.
240 return int(IPsecProto.SEC_AH)
243 def vpp_ipsec_select_backend(node, protocol, index=1):
244 """Select IPsec backend.
246 :param node: VPP node to select IPsec backend on.
247 :param protocol: IPsec protocol.
248 :param index: Backend index.
250 :type protocol: IPsecProto
252 :raises RuntimeError: If failed to select IPsec backend or if no API
255 cmd = u"ipsec_select_backend"
256 err_msg = f"Failed to select IPsec backend on host {node[u'host']}"
261 with PapiSocketExecutor(node) as papi_exec:
262 papi_exec.add(cmd, **args).get_reply(err_msg)
265 def vpp_ipsec_add_sad_entry(
266 node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
267 integ_key=u"", tunnel_src=None, tunnel_dst=None):
268 """Create Security Association Database entry on the VPP node.
270 :param node: VPP node to add SAD entry on.
271 :param sad_id: SAD entry ID.
272 :param spi: Security Parameter Index of this SAD entry.
273 :param crypto_alg: The encryption algorithm name.
274 :param crypto_key: The encryption key string.
275 :param integ_alg: The integrity algorithm name.
276 :param integ_key: The integrity key string.
277 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
278 specified ESP transport mode is used.
279 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
280 not specified ESP transport mode is used.
284 :type crypto_alg: CryptoAlg
285 :type crypto_key: str
286 :type integ_alg: IntegAlg
288 :type tunnel_src: str
289 :type tunnel_dst: str
291 if isinstance(crypto_key, str):
292 crypto_key = crypto_key.encode(encoding=u"utf-8")
293 if isinstance(integ_key, str):
294 integ_key = integ_key.encode(encoding=u"utf-8")
296 length=len(crypto_key),
300 length=len(integ_key),
301 data=integ_key if integ_key else 0
304 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
305 if tunnel_src and tunnel_dst:
306 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
307 src_addr = ip_address(tunnel_src)
308 dst_addr = ip_address(tunnel_dst)
309 if src_addr.version == 6:
311 flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
316 cmd = u"ipsec_sad_entry_add_del"
317 err_msg = f"Failed to add Security Association Database entry " \
318 f"on host {node[u'host']}"
322 crypto_algorithm=crypto_alg.alg_int_repr,
324 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
327 tunnel_src=str(src_addr),
328 tunnel_dst=str(dst_addr),
329 protocol=int(IPsecProto.ESP)
335 with PapiSocketExecutor(node) as papi_exec:
336 papi_exec.add(cmd, **args).get_reply(err_msg)
339 def vpp_ipsec_add_sad_entries(
340 node, n_entries, sad_id, spi, crypto_alg, crypto_key,
341 integ_alg=None, integ_key=u"", tunnel_src=None, tunnel_dst=None):
342 """Create multiple Security Association Database entries on VPP node.
344 :param node: VPP node to add SAD entry on.
345 :param n_entries: Number of SAD entries to be created.
346 :param sad_id: First SAD entry ID. All subsequent SAD entries will have
348 :param spi: Security Parameter Index of first SAD entry. All subsequent
349 SAD entries will have spi incremented by 1.
350 :param crypto_alg: The encryption algorithm name.
351 :param crypto_key: The encryption key string.
352 :param integ_alg: The integrity algorithm name.
353 :param integ_key: The integrity key string.
354 :param tunnel_src: Tunnel header source IPv4 or IPv6 address. If not
355 specified ESP transport mode is used.
356 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. If
357 not specified ESP transport mode is used.
362 :type crypto_alg: CryptoAlg
363 :type crypto_key: str
364 :type integ_alg: IntegAlg
366 :type tunnel_src: str
367 :type tunnel_dst: str
369 if isinstance(crypto_key, str):
370 crypto_key = crypto_key.encode(encoding=u"utf-8")
371 if isinstance(integ_key, str):
372 integ_key = integ_key.encode(encoding=u"utf-8")
373 if tunnel_src and tunnel_dst:
374 src_addr = ip_address(tunnel_src)
375 dst_addr = ip_address(tunnel_dst)
380 addr_incr = 1 << (128 - 96) if src_addr.version == 6 \
383 if int(n_entries) > 10:
384 tmp_filename = f"/tmp/ipsec_sad_{sad_id}_add_del_entry.script"
386 with open(tmp_filename, 'w') as tmp_file:
387 for i in range(n_entries):
388 integ = f"integ-alg {integ_alg.alg_name} " \
389 f"integ-key {integ_key.hex()}" \
390 if integ_alg else u""
391 tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
392 f"tunnel-dst {dst_addr + i * addr_incr}" \
393 if tunnel_src and tunnel_dst else u""
394 conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
395 f"crypto-alg {crypto_alg.alg_name} " \
396 f"crypto-key {crypto_key.hex()} " \
397 f"{integ} {tunnel}\n"
401 tmp_filename, node, timeout=300, json_out=False,
404 os.remove(tmp_filename)
408 length=len(crypto_key),
412 length=len(integ_key),
413 data=integ_key if integ_key else 0
416 flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE)
417 if tunnel_src and tunnel_dst:
418 flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL)
419 if src_addr.version == 6:
421 IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
424 cmd = u"ipsec_sad_entry_add_del"
425 err_msg = f"Failed to add Security Association Database entry " \
426 f"on host {node[u'host']}"
431 crypto_algorithm=crypto_alg.alg_int_repr,
433 integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
436 tunnel_src=str(src_addr),
437 tunnel_dst=str(dst_addr),
438 protocol=int(IPsecProto.ESP)
444 with PapiSocketExecutor(node) as papi_exec:
445 for i in range(n_entries):
446 args[u"entry"][u"sad_id"] = int(sad_id) + i
447 args[u"entry"][u"spi"] = int(spi) + i
448 args[u"entry"][u"tunnel_src"] = str(src_addr + i * addr_incr) \
449 if tunnel_src and tunnel_dst else src_addr
450 args[u"entry"][u"tunnel_dst"] = str(dst_addr + i * addr_incr) \
451 if tunnel_src and tunnel_dst else dst_addr
452 history = bool(not 1 < i < n_entries - 2)
453 papi_exec.add(cmd, history=history, **args)
454 papi_exec.get_replies(err_msg)
457 def vpp_ipsec_set_ip_route(
458 node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
460 """Set IP address and route on interface.
462 :param node: VPP node to add config on.
463 :param n_tunnels: Number of tunnels to create.
464 :param tunnel_src: Tunnel header source IPv4 or IPv6 address.
465 :param traffic_addr: Traffic destination IP address to route.
466 :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address.
467 :param interface: Interface key on node 1.
468 :param raddr_range: Mask specifying range of Policy selector Remote IP
469 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
473 :type tunnel_src: str
474 :type traffic_addr: str
475 :type tunnel_dst: str
477 :type raddr_range: int
479 laddr = ip_address(tunnel_src)
480 raddr = ip_address(tunnel_dst)
481 taddr = ip_address(traffic_addr)
482 addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \
483 else 1 << (32 - raddr_range)
485 if int(n_tunnels) > 10:
486 tmp_filename = u"/tmp/ipsec_set_ip.script"
488 with open(tmp_filename, 'w') as tmp_file:
489 if_name = Topology.get_interface_name(node, interface)
490 for i in range(n_tunnels):
491 conf = f"exec set interface ip address {if_name} " \
492 f"{laddr + i * addr_incr}/{raddr_range}\n" \
493 f"exec ip route add {taddr + i}/" \
494 f"{128 if taddr.version == 6 else 32} " \
495 f"via {raddr + i * addr_incr} {if_name}\n"
499 tmp_filename, node, timeout=300, json_out=False,
502 os.remove(tmp_filename)
505 cmd1 = u"sw_interface_add_del_address"
507 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
512 cmd2 = u"ip_route_add_del"
518 err_msg = f"Failed to configure IP addresses and IP routes " \
519 f"on interface {interface} on host {node[u'host']}"
521 with PapiSocketExecutor(node) as papi_exec:
522 for i in range(n_tunnels):
523 args1[u"prefix"] = IPUtil.create_prefix_object(
524 laddr + i * addr_incr, raddr_range
526 args2[u"route"] = IPUtil.compose_vpp_route_structure(
528 prefix_len=128 if taddr.version == 6 else 32,
529 interface=interface, gateway=raddr + i * addr_incr
531 history = bool(not 1 < i < n_tunnels - 2)
532 papi_exec.add(cmd1, history=history, **args1).\
533 add(cmd2, history=history, **args2)
534 papi_exec.get_replies(err_msg)
537 def vpp_ipsec_add_spd(node, spd_id):
538 """Create Security Policy Database on the VPP node.
540 :param node: VPP node to add SPD on.
541 :param spd_id: SPD ID.
545 cmd = u"ipsec_spd_add_del"
546 err_msg = f"Failed to add Security Policy Database " \
547 f"on host {node[u'host']}"
552 with PapiSocketExecutor(node) as papi_exec:
553 papi_exec.add(cmd, **args).get_reply(err_msg)
556 def vpp_ipsec_spd_add_if(node, spd_id, interface):
557 """Add interface to the Security Policy Database.
559 :param node: VPP node.
560 :param spd_id: SPD ID to add interface on.
561 :param interface: Interface name or sw_if_index.
564 :type interface: str or int
566 cmd = u"ipsec_interface_add_del_spd"
567 err_msg = f"Failed to add interface {interface} to Security Policy " \
568 f"Database {spd_id} on host {node[u'host']}"
571 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
574 with PapiSocketExecutor(node) as papi_exec:
575 papi_exec.add(cmd, **args).get_reply(err_msg)
578 def vpp_ipsec_policy_add(
579 node, spd_id, priority, action, inbound=True, sa_id=None,
580 laddr_range=None, raddr_range=None, proto=None, lport_range=None,
581 rport_range=None, is_ipv6=False):
582 """Create Security Policy Database entry on the VPP node.
584 :param node: VPP node to add SPD entry on.
585 :param spd_id: SPD ID to add entry on.
586 :param priority: SPD entry priority, higher number = higher priority.
587 :param action: Policy action.
588 :param inbound: If True policy is for inbound traffic, otherwise
590 :param sa_id: SAD entry ID for protect action.
591 :param laddr_range: Policy selector local IPv4 or IPv6 address range in
592 format IP/prefix or IP/mask. If no mask is provided,
593 it's considered to be /32.
594 :param raddr_range: Policy selector remote IPv4 or IPv6 address range in
595 format IP/prefix or IP/mask. If no mask is provided,
596 it's considered to be /32.
597 :param proto: Policy selector next layer protocol number.
598 :param lport_range: Policy selector local TCP/UDP port range in format
599 <port_start>-<port_end>.
600 :param rport_range: Policy selector remote TCP/UDP port range in format
601 <port_start>-<port_end>.
602 :param is_ipv6: True in case of IPv6 policy when IPv6 address range is
603 not defined so it will default to address ::/0, otherwise False.
607 :type action: PolicyAction
610 :type laddr_range: string
611 :type raddr_range: string
613 :type lport_range: string
614 :type rport_range: string
617 if laddr_range is None:
618 laddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
620 if raddr_range is None:
621 raddr_range = u"::/0" if is_ipv6 else u"0.0.0.0/0"
623 cmd = u"ipsec_spd_entry_add_del"
624 err_msg = f"Failed to add entry to Security Policy Database {spd_id} " \
625 f"on host {node[u'host']}"
629 priority=int(priority),
630 is_outbound=0 if inbound else 1,
631 sa_id=int(sa_id) if sa_id else 0,
632 policy=action.policy_int_repr,
633 protocol=int(proto) if proto else 0,
634 remote_address_start=IPUtil.create_ip_address_object(
635 ip_network(raddr_range, strict=False).network_address
637 remote_address_stop=IPUtil.create_ip_address_object(
638 ip_network(raddr_range, strict=False).broadcast_address
640 local_address_start=IPUtil.create_ip_address_object(
641 ip_network(laddr_range, strict=False).network_address
643 local_address_stop=IPUtil.create_ip_address_object(
644 ip_network(laddr_range, strict=False).broadcast_address
646 remote_port_start=int(rport_range.split(u"-")[0]) if rport_range
648 remote_port_stop=int(rport_range.split(u"-")[1]) if rport_range
650 local_port_start=int(lport_range.split(u"-")[0]) if lport_range
652 local_port_stop=int(lport_range.split(u"-")[1]) if rport_range
659 with PapiSocketExecutor(node) as papi_exec:
660 papi_exec.add(cmd, **args).get_reply(err_msg)
663 def vpp_ipsec_spd_add_entries(
664 node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip,
666 """Create multiple Security Policy Database entries on the VPP node.
668 :param node: VPP node to add SPD entries on.
669 :param n_entries: Number of SPD entries to be added.
670 :param spd_id: SPD ID to add entries on.
671 :param priority: SPD entries priority, higher number = higher priority.
672 :param inbound: If True policy is for inbound traffic, otherwise
674 :param sa_id: SAD entry ID for first entry. Each subsequent entry will
675 SAD entry ID incremented by 1.
676 :param raddr_ip: Policy selector remote IPv4 start address for the first
677 entry. Remote IPv4 end address will be calculated depending on
678 raddr_range parameter. Each subsequent entry will have start address
679 next after IPv4 end address of previous entry.
680 :param raddr_range: Required IP addres range.
688 :type raddr_range: int
690 raddr_ip = ip_address(raddr_ip)
691 if int(n_entries) > 10:
692 tmp_filename = f"/tmp/ipsec_spd_{sa_id}_add_del_entry.script"
694 with open(tmp_filename, 'w') as tmp_file:
695 for i in range(n_entries):
696 direction = u'inbound' if inbound else u'outbound'
697 tunnel = f"exec ipsec policy add spd {spd_id} " \
698 f"priority {priority} {direction} " \
699 f"action protect sa {sa_id+i} " \
700 f"remote-ip-range {raddr_ip + i * (raddr_range + 1)} " \
701 f"- {raddr_ip + (i + 1) * raddr_range + i} " \
702 f"local-ip-range 0.0.0.0 - 255.255.255.255\n"
703 tmp_file.write(tunnel)
704 VatExecutor().execute_script(
705 tmp_filename, node, timeout=300, json_out=False,
708 os.remove(tmp_filename)
711 laddr_range = u"::/0" if raddr_ip.version == 6 else u"0.0.0.0/0"
713 cmd = u"ipsec_spd_entry_add_del"
714 err_msg = f"ailed to add entry to Security Policy Database '{spd_id} " \
715 f"on host {node[u'host']}"
719 priority=int(priority),
720 is_outbound=0 if inbound else 1,
721 sa_id=int(sa_id) if sa_id else 0,
722 policy=IPsecUtil.policy_action_protect().policy_int_repr,
724 remote_address_start=IPUtil.create_ip_address_object(raddr_ip),
725 remote_address_stop=IPUtil.create_ip_address_object(raddr_ip),
726 local_address_start=IPUtil.create_ip_address_object(
727 ip_network(laddr_range, strict=False).network_address
729 local_address_stop=IPUtil.create_ip_address_object(
730 ip_network(laddr_range, strict=False).broadcast_address
733 remote_port_stop=65535,
735 local_port_stop=65535
742 with PapiSocketExecutor(node) as papi_exec:
743 for i in range(n_entries):
744 args[u"entry"][u"remote_address_start"][u"un"] = \
745 IPUtil.union_addr(raddr_ip + i)
746 args[u"entry"][u"remote_address_stop"][u"un"] = \
747 IPUtil.union_addr(raddr_ip + i)
748 history = bool(not 1 < i < n_entries - 2)
749 papi_exec.add(cmd, history=history, **args)
750 papi_exec.get_replies(err_msg)
753 def vpp_ipsec_create_tunnel_interfaces(
754 nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels,
755 crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range):
756 """Create multiple IPsec tunnel interfaces between two VPP nodes.
758 :param nodes: VPP nodes to create tunnel interfaces.
759 :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
760 :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address.
761 :param if1_key: VPP node 1 interface key from topology file.
762 :param if2_key: VPP node 2 interface key from topology file.
763 :param n_tunnels: Number of tunnel interfaces to create.
764 :param crypto_alg: The encryption algorithm name.
765 :param integ_alg: The integrity algorithm name.
766 :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the
767 first tunnel in direction node1->node2.
768 :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the
769 first tunnel in direction node2->node1.
770 :param raddr_range: Mask specifying range of Policy selector Remote
771 IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4
772 and to 128 in case of IPv6.
774 :type if1_ip_addr: str
775 :type if2_ip_addr: str
779 :type crypto_alg: CryptoAlg
780 :type integ_alg: IntegAlg
781 :type raddr_ip1: string
782 :type raddr_ip2: string
783 :type raddr_range: int
785 n_tunnels = int(n_tunnels)
788 if1_ip = ip_address(if1_ip_addr)
789 if2_ip = ip_address(if2_ip_addr)
790 raddr_ip1 = ip_address(raddr_ip1)
791 raddr_ip2 = ip_address(raddr_ip2)
792 addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \
793 else 1 << (32 - raddr_range)
796 tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
797 tmp_fn2 = u"/tmp/ipsec_create_tunnel_dut2.config"
798 if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
799 if2_n = Topology.get_interface_name(nodes[u"DUT2"], if2_key)
800 mask = 96 if if2_ip.version == 6 else 24
801 mask2 = 128 if if2_ip.version == 6 else 32
803 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
804 rmac = Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
806 f"exec create loopback interface\n"
807 f"exec set interface state loop0 up\n"
808 f"exec set interface ip address "
809 f"{if1_n} {if2_ip - 1}/{mask}\n"
810 f"exec set ip arp {if1_n} {if2_ip}/{mask2} {rmac} static\n"
813 f"exec set interface ip address {if2_n} {if2_ip}/{mask}\n"
815 for i in range(n_tunnels):
817 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
821 IPsecUtil.get_integ_alg_key_len(integ_alg)
823 integ = f"integ_alg {integ_alg.alg_name} " \
824 f"local_integ_key {ikey} remote_integ_key {ikey} "
828 f"exec set interface ip address loop0 "
829 f"{if1_ip + i * addr_incr}/32\n"
830 f"ipsec_tunnel_if_add_del "
831 f"local_spi {spi_1 + i} remote_spi {spi_2 + i} "
832 f"crypto_alg {crypto_alg.alg_name} "
833 f"local_crypto_key {ckey} remote_crypto_key {ckey} "
835 f"local_ip {if1_ip + i * addr_incr} "
836 f"remote_ip {if2_ip} "
840 f"ipsec_tunnel_if_add_del "
841 f"local_spi {spi_2 + i} remote_spi {spi_1 + i} "
842 f"crypto_alg {crypto_alg.alg_name} "
843 f"local_crypto_key {ckey} remote_crypto_key {ckey} "
845 f"local_ip {if2_ip} "
846 f"remote_ip {if1_ip + i * addr_incr} "
850 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
851 copy_on_execute=True,
852 history=bool(n_tunnels < 100)
855 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
856 copy_on_execute=True,
857 history=bool(n_tunnels < 100)
862 with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2:
864 f"exec ip route add {if1_ip}/8 via {if2_ip - 1} {if2_n}\n"
866 for i in range(n_tunnels):
868 f"exec set interface unnumbered ipip{i} use {if1_n}\n"
869 f"exec set interface state ipip{i} up\n"
870 f"exec ip route add {raddr_ip2 + i}/{mask2} "
874 f"exec set interface unnumbered ipip{i} use {if2_n}\n"
875 f"exec set interface state ipip{i} up\n"
876 f"exec ip route add {raddr_ip1 + i}/{mask2} "
880 tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
881 copy_on_execute=True,
882 history=bool(n_tunnels < 100)
885 tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
886 copy_on_execute=True,
887 history=bool(n_tunnels < 100)
893 with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
894 # Create loopback interface on DUT1, set it to up state
895 cmd1 = u"create_loopback"
899 err_msg = f"Failed to create loopback interface " \
900 f"on host {nodes[u'DUT1'][u'host']}"
901 loop_sw_if_idx = papi_exec.add(cmd1, **args1).\
902 get_sw_if_index(err_msg)
903 cmd1 = u"sw_interface_set_flags"
905 sw_if_index=loop_sw_if_idx,
906 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
908 err_msg = f"Failed to set loopback interface state up " \
909 f"on host {nodes[u'DUT1'][u'host']}"
910 papi_exec.add(cmd1, **args1).get_reply(err_msg)
911 # Set IP address on VPP node 1 interface
912 cmd1 = u"sw_interface_add_del_address"
914 sw_if_index=InterfaceUtil.get_interface_index(
915 nodes[u"DUT1"], if1_key
919 prefix=IPUtil.create_prefix_object(
920 if2_ip - 1, 96 if if2_ip.version == 6 else 24
923 err_msg = f"Failed to set IP address on interface {if1_key} " \
924 f"on host {nodes[u'DUT1'][u'host']}"
925 papi_exec.add(cmd1, **args1).get_reply(err_msg)
926 cmd4 = u"ip_neighbor_add_del"
930 sw_if_index=Topology.get_interface_sw_index(
931 nodes[u"DUT1"], if1_key
935 Topology.get_interface_mac(nodes[u"DUT2"], if2_key)
937 ip_address=str(ip_address(if2_ip_addr))
940 err_msg = f"Failed to add IP neighbor on interface {if1_key}"
941 papi_exec.add(cmd4, **args4).get_reply(err_msg)
942 # Configure IPsec tunnel interfaces
944 sw_if_index=loop_sw_if_idx,
949 cmd2 = u"ipsec_tunnel_if_add_del"
956 crypto_alg=crypto_alg.alg_int_repr,
957 local_crypto_key_len=0,
958 local_crypto_key=None,
959 remote_crypto_key_len=0,
960 remote_crypto_key=None,
961 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
962 local_integ_key_len=0,
963 local_integ_key=None,
964 remote_integ_key_len=0,
965 remote_integ_key=None,
968 err_msg = f"Failed to add IPsec tunnel interfaces " \
969 f"on host {nodes[u'DUT1'][u'host']}"
970 ipsec_tunnels = list()
973 for i in range(n_tunnels):
975 gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
979 gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
981 args1[u"prefix"] = IPUtil.create_prefix_object(
982 if1_ip + i * addr_incr, 128 if if1_ip.version == 6 else 32
984 args2[u"local_spi"] = spi_1 + i
985 args2[u"remote_spi"] = spi_2 + i
986 args2[u"local_ip"] = IPUtil.create_ip_address_object(
987 if1_ip + i * addr_incr
989 args2[u"remote_ip"] = IPUtil.create_ip_address_object(if2_ip)
990 args2[u"local_crypto_key_len"] = len(ckeys[i])
991 args2[u"local_crypto_key"] = ckeys[i]
992 args2[u"remote_crypto_key_len"] = len(ckeys[i])
993 args2[u"remote_crypto_key"] = ckeys[i]
995 args2[u"local_integ_key_len"] = len(ikeys[i])
996 args2[u"local_integ_key"] = ikeys[i]
997 args2[u"remote_integ_key_len"] = len(ikeys[i])
998 args2[u"remote_integ_key"] = ikeys[i]
999 history = bool(not 1 < i < n_tunnels - 2)
1000 papi_exec.add(cmd1, history=history, **args1).\
1001 add(cmd2, history=history, **args2)
1002 replies = papi_exec.get_replies(err_msg)
1003 for reply in replies:
1004 if u"sw_if_index" in reply:
1005 ipsec_tunnels.append(reply[u"sw_if_index"])
1006 # Configure IP routes
1007 cmd1 = u"sw_interface_set_unnumbered"
1010 sw_if_index=InterfaceUtil.get_interface_index(
1011 nodes[u"DUT1"], if1_key
1013 unnumbered_sw_if_index=0
1015 cmd2 = u"sw_interface_set_flags"
1018 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1020 cmd3 = u"ip_route_add_del"
1026 err_msg = f"Failed to add IP routes " \
1027 f"on host {nodes[u'DUT1'][u'host']}"
1028 for i in range(n_tunnels):
1029 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1030 args2[u"sw_if_index"] = ipsec_tunnels[i]
1031 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1032 nodes[u"DUT1"], (raddr_ip2 + i).compressed,
1033 prefix_len=128 if raddr_ip2.version == 6 else 32,
1034 interface=ipsec_tunnels[i]
1036 history = bool(not 1 < i < n_tunnels - 2)
1037 papi_exec.add(cmd1, history=history, **args1).\
1038 add(cmd2, history=history, **args2).\
1039 add(cmd3, history=history, **args3)
1040 papi_exec.get_replies(err_msg)
1042 with PapiSocketExecutor(nodes[u"DUT2"]) as papi_exec:
1043 # Set IP address on VPP node 2 interface
1044 cmd1 = u"sw_interface_add_del_address"
1046 sw_if_index=InterfaceUtil.get_interface_index(
1047 nodes[u"DUT2"], if2_key
1051 prefix=IPUtil.create_prefix_object(
1052 if2_ip, 96 if if2_ip.version == 6 else 24
1055 err_msg = f"Failed to set IP address on interface {if2_key} " \
1056 f"on host {nodes[u'DUT2'][u'host']}"
1057 papi_exec.add(cmd1, **args1).get_reply(err_msg)
1058 # Configure IPsec tunnel interfaces
1059 cmd2 = u"ipsec_tunnel_if_add_del"
1062 local_ip=IPUtil.create_ip_address_object(if2_ip),
1066 crypto_alg=crypto_alg.alg_int_repr,
1067 local_crypto_key_len=0,
1068 local_crypto_key=None,
1069 remote_crypto_key_len=0,
1070 remote_crypto_key=None,
1071 integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
1072 local_integ_key_len=0,
1073 local_integ_key=None,
1074 remote_integ_key_len=0,
1075 remote_integ_key=None,
1078 err_msg = f"Failed to add IPsec tunnel interfaces " \
1079 f"on host {nodes[u'DUT2'][u'host']}"
1080 ipsec_tunnels = list()
1081 for i in range(n_tunnels):
1082 args2[u"local_spi"] = spi_2 + i
1083 args2[u"remote_spi"] = spi_1 + i
1084 args2[u"local_ip"] = IPUtil.create_ip_address_object(if2_ip)
1085 args2[u"remote_ip"] = IPUtil.create_ip_address_object(
1086 if1_ip + i * addr_incr)
1087 args2[u"local_crypto_key_len"] = len(ckeys[i])
1088 args2[u"local_crypto_key"] = ckeys[i]
1089 args2[u"remote_crypto_key_len"] = len(ckeys[i])
1090 args2[u"remote_crypto_key"] = ckeys[i]
1092 args2[u"local_integ_key_len"] = len(ikeys[i])
1093 args2[u"local_integ_key"] = ikeys[i]
1094 args2[u"remote_integ_key_len"] = len(ikeys[i])
1095 args2[u"remote_integ_key"] = ikeys[i]
1096 history = bool(not 1 < i < n_tunnels - 2)
1097 papi_exec.add(cmd2, history=history, **args2)
1098 replies = papi_exec.get_replies(err_msg)
1099 for reply in replies:
1100 if u"sw_if_index" in reply:
1101 ipsec_tunnels.append(reply[u"sw_if_index"])
1102 # Configure IP routes
1103 cmd1 = u"ip_route_add_del"
1104 route = IPUtil.compose_vpp_route_structure(
1105 nodes[u"DUT2"], if1_ip.compressed,
1106 prefix_len=32 if if1_ip.version == 6 else 8,
1108 gateway=(if2_ip - 1).compressed
1115 papi_exec.add(cmd1, **args1)
1116 cmd1 = u"sw_interface_set_unnumbered"
1119 sw_if_index=InterfaceUtil.get_interface_index(
1120 nodes[u"DUT2"], if2_key
1122 unnumbered_sw_if_index=0
1124 cmd2 = u"sw_interface_set_flags"
1127 flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
1129 cmd3 = u"ip_route_add_del"
1135 err_msg = f"Failed to add IP routes " \
1136 f"on host {nodes[u'DUT2'][u'host']}"
1137 for i in range(n_tunnels):
1138 args1[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
1139 args2[u"sw_if_index"] = ipsec_tunnels[i]
1140 args3[u"route"] = IPUtil.compose_vpp_route_structure(
1141 nodes[u"DUT1"], (raddr_ip1 + i).compressed,
1142 prefix_len=128 if raddr_ip1.version == 6 else 32,
1143 interface=ipsec_tunnels[i]
1145 history = bool(not 1 < i < n_tunnels - 2)
1146 papi_exec.add(cmd1, history=history, **args1). \
1147 add(cmd2, history=history, **args2). \
1148 add(cmd3, history=history, **args3)
1149 papi_exec.get_replies(err_msg)
1152 def vpp_ipsec_add_multiple_tunnels(
1153 nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg,
1154 tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range):
1155 """Create multiple IPsec tunnels between two VPP nodes.
1157 :param nodes: VPP nodes to create tunnels.
1158 :param interface1: Interface name or sw_if_index on node 1.
1159 :param interface2: Interface name or sw_if_index on node 2.
1160 :param n_tunnels: Number of tunnels to create.
1161 :param crypto_alg: The encryption algorithm name.
1162 :param integ_alg: The integrity algorithm name.
1163 :param tunnel_ip1: Tunnel node1 IPv4 address.
1164 :param tunnel_ip2: Tunnel node2 IPv4 address.
1165 :param raddr_ip1: Policy selector remote IPv4 start address for the
1166 first tunnel in direction node1->node2.
1167 :param raddr_ip2: Policy selector remote IPv4 start address for the
1168 first tunnel in direction node2->node1.
1169 :param raddr_range: Mask specifying range of Policy selector Remote IPv4
1170 addresses. Valid values are from 1 to 32.
1172 :type interface1: str or int
1173 :type interface2: str or int
1174 :type n_tunnels: int
1175 :type crypto_alg: CryptoAlg
1176 :type integ_alg: IntegAlg
1177 :type tunnel_ip1: str
1178 :type tunnel_ip2: str
1179 :type raddr_ip1: string
1180 :type raddr_ip2: string
1181 :type raddr_range: int
1191 crypto_key = gen_key(
1192 IPsecUtil.get_crypto_alg_key_len(crypto_alg)
1194 integ_key = gen_key(
1195 IPsecUtil.get_integ_alg_key_len(integ_alg)
1196 ).decode() if integ_alg else u""
1198 IPsecUtil.vpp_ipsec_set_ip_route(
1199 nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
1200 interface1, raddr_range)
1201 IPsecUtil.vpp_ipsec_set_ip_route(
1202 nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
1203 interface2, raddr_range)
1205 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
1206 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
1207 IPsecUtil.vpp_ipsec_policy_add(
1208 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1209 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1211 IPsecUtil.vpp_ipsec_policy_add(
1212 nodes[u"DUT1"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1213 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1216 IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
1217 IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
1218 IPsecUtil.vpp_ipsec_policy_add(
1219 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
1220 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1222 IPsecUtil.vpp_ipsec_policy_add(
1223 nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
1224 proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
1227 IPsecUtil.vpp_ipsec_add_sad_entries(
1228 nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1229 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1231 IPsecUtil.vpp_ipsec_spd_add_entries(
1232 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2
1235 IPsecUtil.vpp_ipsec_add_sad_entries(
1236 nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
1237 integ_alg, integ_key, tunnel_ip1, tunnel_ip2
1239 IPsecUtil.vpp_ipsec_spd_add_entries(
1240 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
1243 IPsecUtil.vpp_ipsec_add_sad_entries(
1244 nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1245 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1248 IPsecUtil.vpp_ipsec_spd_add_entries(
1249 nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
1252 IPsecUtil.vpp_ipsec_add_sad_entries(
1253 nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
1254 integ_alg, integ_key, tunnel_ip2, tunnel_ip1
1257 IPsecUtil.vpp_ipsec_spd_add_entries(
1258 nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
1262 def vpp_ipsec_show(node):
1263 """Run "show ipsec" debug CLI command.
1265 :param node: Node to run command on.
1268 PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")