-# Copyright (c) 2020 Cisco and/or its affiliates.
+# Copyright (c) 2021 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
from ipaddress import ip_network, ip_address
+from resources.libraries.python.Constants import Constants
from resources.libraries.python.InterfaceUtil import InterfaceUtil, \
InterfaceStatusFlags
from resources.libraries.python.IPAddress import IPAddress
-from resources.libraries.python.IPUtil import IPUtil
+from resources.libraries.python.IPUtil import IPUtil, IpDscp, MPLS_LABEL_INVALID
from resources.libraries.python.PapiExecutor import PapiSocketExecutor
from resources.libraries.python.ssh import scp_node
from resources.libraries.python.topology import Topology
from resources.libraries.python.VatExecutor import VatExecutor
+IPSEC_UDP_PORT_NONE = 0xffff
+
+
def gen_key(length):
"""Generate random string as a key.
class IPsecSadFlags(IntEnum):
"""IPsec Security Association Database flags."""
- IPSEC_API_SAD_FLAG_NONE = 0
- IPSEC_API_SAD_FLAG_IS_TUNNEL = 4
- IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 8
+ IPSEC_API_SAD_FLAG_NONE = 0,
+ # Enable extended sequence numbers
+ IPSEC_API_SAD_FLAG_USE_ESN = 0x01,
+ # Enable Anti - replay
+ IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY = 0x02,
+ # IPsec tunnel mode if non-zero, else transport mode
+ IPSEC_API_SAD_FLAG_IS_TUNNEL = 0x04,
+ # IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel
+ # only valid if is_tunnel is non-zero
+ IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08,
+ # Enable UDP encapsulation for NAT traversal
+ IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10,
+ # IPsec SA is or inbound traffic
+ IPSEC_API_SAD_FLAG_IS_INBOUND = 0x40
+
+
+class TunnelEncpaDecapFlags(IntEnum):
+ """Flags controlling tunnel behaviour."""
+ TUNNEL_API_ENCAP_DECAP_FLAG_NONE = 0
+ # at encap, copy the DF bit of the payload into the tunnel header
+ TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF = 1
+ # at encap, set the DF bit in the tunnel header
+ TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF = 2
+ # at encap, copy the DSCP bits of the payload into the tunnel header
+ TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP = 4
+ # at encap, copy the ECN bit of the payload into the tunnel header
+ TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN = 8
+ # at decap, copy the ECN bit of the tunnel header into the payload
+ TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_ECN = 16
+
+
+class TunnelMode(IntEnum):
+ """Tunnel modes."""
+ # point-to-point
+ TUNNEL_API_MODE_P2P = 0
+ # multi-point
+ TUNNEL_API_MODE_MP = 1
class IPsecUtil:
def get_integ_alg_key_len(integ_alg):
"""Return integrity algorithm key length.
+ None argument is accepted, returning zero.
+
:param integ_alg: Integrity algorithm.
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:returns: Key length.
:rtype: int
"""
- return integ_alg.key_len
+ return 0 if integ_alg is None else integ_alg.key_len
@staticmethod
def get_integ_alg_scapy_name(integ_alg):
with PapiSocketExecutor(node) as papi_exec:
papi_exec.add(cmd, **args).get_reply(err_msg)
+ @staticmethod
+ def vpp_ipsec_crypto_sw_scheduler_set_worker(
+ node, worker_index, crypto_enable=False):
+ """Enable or disable crypto on specific vpp worker threads.
+
+ :param node: VPP node to enable or disable crypto for worker threads.
+ :param worker_index: VPP worker thread index.
+ :param crypto_enable: Disable or enable crypto work.
+ :type node: dict
+ :type worker_index: int
+ :type crypto_enable: bool
+ :raises RuntimeError: If failed to enable or disable crypto for worker
+ thread or if no API reply received.
+ """
+ cmd = u"crypto_sw_scheduler_set_worker"
+ err_msg = f"Failed to disable/enable crypto for worker thread " \
+ f"on host {node[u'host']}"
+ args = dict(
+ worker_index=worker_index,
+ crypto_enable=crypto_enable
+ )
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args).get_reply(err_msg)
+
@staticmethod
def vpp_ipsec_add_sad_entry(
node, sad_id, spi, crypto_alg, crypto_key, integ_alg=None,
:type spi: int
:type crypto_alg: CryptoAlg
:type crypto_key: str
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:type integ_key: str
:type tunnel_src: str
:type tunnel_dst: str
src_addr = u""
dst_addr = u""
- cmd = u"ipsec_sad_entry_add_del"
+ cmd = u"ipsec_sad_entry_add_del_v2"
err_msg = f"Failed to add Security Association Database entry " \
f"on host {node[u'host']}"
sad_entry = dict(
flags=flags,
tunnel_src=str(src_addr),
tunnel_dst=str(dst_addr),
+ tunnel_flags=int(
+ TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+ ),
+ dscp=int(IpDscp.IP_API_DSCP_CS0),
protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
udp_src_port=4500, # default value in api
udp_dst_port=4500 # default value in api
:type spi: int
:type crypto_alg: CryptoAlg
:type crypto_key: str
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:type integ_key: str
:type tunnel_src: str
:type tunnel_dst: str
integ = f"integ-alg {integ_alg.alg_name} " \
f"integ-key {integ_key.hex()}" \
if integ_alg else u""
- tunnel = f"tunnel-src {src_addr + i * addr_incr} " \
- f"tunnel-dst {dst_addr + i * addr_incr}" \
+ tunnel = f"tunnel src {src_addr + i * addr_incr} " \
+ f"tunnel dst {dst_addr + i * addr_incr}" \
if tunnel_src and tunnel_dst else u""
conf = f"exec ipsec sa add {sad_id + i} esp spi {spi + i} "\
f"crypto-alg {crypto_alg.alg_name} " \
IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6
)
- cmd = u"ipsec_sad_entry_add_del"
+ cmd = u"ipsec_sad_entry_add_del_v2"
err_msg = f"Failed to add Security Association Database entry " \
f"on host {node[u'host']}"
flags=flags,
tunnel_src=str(src_addr),
tunnel_dst=str(dst_addr),
+ tunnel_flags=int(
+ TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+ ),
+ dscp=int(IpDscp.IP_API_DSCP_CS0),
protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
udp_src_port=4500, # default value in api
udp_dst_port=4500 # default value in api
@staticmethod
def vpp_ipsec_set_ip_route(
node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface,
- raddr_range):
+ raddr_range, dst_mac=None):
"""Set IP address and route on interface.
:param node: VPP node to add config on.
:param raddr_range: Mask specifying range of Policy selector Remote IP
addresses. Valid values are from 1 to 32 in case of IPv4 and to 128
in case of IPv6.
+ :param dst_mac: The MAC address of destination tunnels.
:type node: dict
:type n_tunnels: int
:type tunnel_src: str
:type tunnel_dst: str
:type interface: str
:type raddr_range: int
+ :type dst_mac: str
"""
tunnel_src = ip_address(tunnel_src)
tunnel_dst = ip_address(tunnel_dst)
f"exec ip route add {traffic_addr + i}/" \
f"{128 if traffic_addr.version == 6 else 32} " \
f"via {tunnel_dst + i * addr_incr} {if_name}\n"
+ if dst_mac:
+ conf = f"{conf}exec set ip neighbor {if_name} " \
+ f"{tunnel_dst + i * addr_incr} {dst_mac}\n"
tmp_file.write(conf)
+
VatExecutor().execute_script(
tmp_filename, node, timeout=300, json_out=False,
copy_on_execute=True
is_multipath=0,
route=None
)
- err_msg = f"Failed to configure IP addresses and IP routes " \
- f"on interface {interface} on host {node[u'host']}"
+ cmd3 = u"ip_neighbor_add_del"
+ args3 = dict(
+ is_add=True,
+ neighbor=dict(
+ sw_if_index=Topology.get_interface_sw_index(node, interface),
+ flags=0,
+ mac_address=str(dst_mac),
+ ip_address=None
+ )
+ )
+ err_msg = f"Failed to configure IP addresses, IP routes and " \
+ f"IP neighbor on interface {interface} on host {node[u'host']}" \
+ if dst_mac \
+ else f"Failed to configure IP addresses and IP routes " \
+ f"on interface {interface} on host {node[u'host']}"
with PapiSocketExecutor(node) as papi_exec:
for i in range(n_tunnels):
history = bool(not 1 < i < n_tunnels - 2)
papi_exec.add(cmd1, history=history, **args1).\
add(cmd2, history=history, **args2)
+ if dst_mac:
+ args3[u"neighbor"][u"ip_address"] = ip_address(
+ tunnel_dst + i * addr_incr
+ )
+ papi_exec.add(cmd3, history=history, **args3)
papi_exec.get_replies(err_msg)
@staticmethod
raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
"""Create multiple IPsec tunnel interfaces on DUT1 node using VAT.
+ Generate random keys and return them (so DUT2 or TG can decrypt).
+
:param nodes: VPP nodes to create tunnel interfaces.
:param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
:type if2_key: str
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:type raddr_ip2: IPv4Address or IPv6Address
:type addr_incr: int
:type spi_d: dict
:type existing_tunnels: int
+ :returns: Generated ckeys and ikeys.
+ :rtype: List[bytes], List[bytes]
"""
tmp_fn1 = u"/tmp/ipsec_create_tunnel_dut1.config"
if1_n = Topology.get_interface_name(nodes[u"DUT1"], if1_key)
ckeys.append(
gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
)
+ ikeys.append(
+ gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
+ )
if integ_alg:
- ikeys.append(
- gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
- )
- integ = f"integ_alg {integ_alg.alg_name} " \
- f"local_integ_key {ikeys[i].hex()} " \
- f"remote_integ_key {ikeys[i].hex()} "
+ integ = f"integ-alg {integ_alg.alg_name} " \
+ f"integ-key {ikeys[i].hex()} "
else:
integ = u""
tmp_f1.write(
f"exec set interface ip address loop0 "
f"{tun_ips[u'ip1'] + i * addr_incr}/32\n"
- f"ipsec_tunnel_if_add_del "
- f"local_spi {spi_d[u'spi_1'] + i} "
- f"remote_spi {spi_d[u'spi_2'] + i} "
- f"crypto_alg {crypto_alg.alg_name} "
- f"local_crypto_key {ckeys[i].hex()} "
- f"remote_crypto_key {ckeys[i].hex()} "
- f"{integ} "
- f"local_ip {tun_ips[u'ip1'] + i * addr_incr} "
- f"remote_ip {tun_ips[u'ip2']} "
- f"instance {i}\n"
+ f"exec create ipip tunnel "
+ f"src {tun_ips[u'ip1'] + i * addr_incr} "
+ f"dst {tun_ips[u'ip2']} "
+ f"p2p\n"
+ f"exec ipsec sa add {i} "
+ f"spi {spi_d[u'spi_1'] + i} "
+ f"crypto-alg {crypto_alg.alg_name} "
+ f"crypto-key {ckeys[i].hex()} "
+ f"{integ}"
+ f"esp\n"
+ f"exec ipsec sa add {100000 + i} "
+ f"spi {spi_d[u'spi_2'] + i} "
+ f"crypto-alg {crypto_alg.alg_name} "
+ f"crypto-key {ckeys[i].hex()} "
+ f"{integ}"
+ f"esp\n"
+ f"exec ipsec tunnel protect ipip{i} "
+ f"sa-out {i} "
+ f"sa-in {100000 + i} "
+ f"add\n"
)
vat.execute_script(
tmp_fn1, nodes[u"DUT1"], timeout=1800, json_out=False,
ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
"""Create multiple IPsec tunnel interfaces on DUT2 node using VAT.
+ This method accesses keys generated by DUT1 method
+ and does not return anything.
+
:param nodes: VPP nodes to create tunnel interfaces.
:param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
:type if2_key: str
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type ckeys: list
- :type integ_alg: IntegAlg
- :type ikeys: list
+ :type ckeys: Sequence[bytes]
+ :type integ_alg: Optional[IntegAlg]
+ :type ikeys: Sequence[bytes]
:type addr_incr: int
:type spi_d: dict
:type existing_tunnels: int
)
for i in range(existing_tunnels, n_tunnels):
if integ_alg:
- integ = f"integ_alg {integ_alg.alg_name} " \
- f"local_integ_key {ikeys[i].hex()} " \
- f"remote_integ_key {ikeys[i].hex()} "
+ integ = f"integ-alg {integ_alg.alg_name} " \
+ f"integ-key {ikeys[i].hex()} "
else:
integ = u""
tmp_f2.write(
- f"ipsec_tunnel_if_add_del "
- f"local_spi {spi_d[u'spi_2'] + i} "
- f"remote_spi {spi_d[u'spi_1'] + i} "
- f"crypto_alg {crypto_alg.alg_name} "
- f"local_crypto_key {ckeys[i].hex()} "
- f"remote_crypto_key {ckeys[i].hex()} "
- f"{integ} "
- f"local_ip {tun_ips[u'ip2']} "
- f"remote_ip {tun_ips[u'ip1'] + i * addr_incr} "
- f"instance {i}\n"
+ f"exec create ipip tunnel "
+ f"src {tun_ips[u'ip2']} "
+ f"dst {tun_ips[u'ip1'] + i * addr_incr} "
+ f"p2p\n"
+ f"exec ipsec sa add {100000 + i} "
+ f"spi {spi_d[u'spi_2'] + i} "
+ f"crypto-alg {crypto_alg.alg_name} "
+ f"crypto-key {ckeys[i].hex()} "
+ f"{integ}"
+ f"esp\n"
+ f"exec ipsec sa add {i} "
+ f"spi {spi_d[u'spi_1'] + i} "
+ f"crypto-alg {crypto_alg.alg_name} "
+ f"crypto-key {ckeys[i].hex()} "
+ f"{integ}"
+ f"esp\n"
+ f"exec ipsec tunnel protect ipip{i} "
+ f"sa-out {100000 + i} "
+ f"sa-in {i} "
+ f"add\n"
)
vat.execute_script(
tmp_fn2, nodes[u"DUT2"], timeout=1800, json_out=False,
"""
with PapiSocketExecutor(nodes[u"DUT1"]) as papi_exec:
# Create loopback interface on DUT1, set it to up state
- cmd = u"create_loopback"
+ cmd = u"create_loopback_instance"
args = dict(
- mac_address=0
+ mac_address=0,
+ is_specified=False,
+ user_instance=0,
)
err_msg = f"Failed to create loopback interface " \
f"on host {nodes[u'DUT1'][u'host']}"
raddr_ip2, addr_incr, spi_d, existing_tunnels=0):
"""Create multiple IPsec tunnel interfaces on DUT1 node using PAPI.
+ Generate random keys and return them (so DUT2 or TG can decrypt).
+
:param nodes: VPP nodes to create tunnel interfaces.
:param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
:type if2_key: str
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:type raddr_ip2: IPv4Address or IPv6Address
:type addr_incr: int
:type spi_d: dict
:type existing_tunnels: int
+ :returns: Generated ckeys and ikeys.
+ :rtype: List[bytes], List[bytes]
"""
if not existing_tunnels:
loop_sw_if_idx = IPsecUtil._ipsec_create_loopback_dut1_papi(
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
- # Configure IPsec tunnel interfaces
- cmd = u"ipsec_tunnel_if_add_del"
+ # Configure IPIP tunnel interfaces
+ cmd = u"ipip_add_tunnel"
+ ipip_tunnel = dict(
+ instance=Constants.BITWISE_NON_ZERO,
+ src=None,
+ dst=None,
+ table_id=0,
+ flags=int(
+ TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+ ),
+ mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
+ dscp=int(IpDscp.IP_API_DSCP_CS0)
+ )
args = dict(
- is_add=True,
- local_ip=None,
- remote_ip=None,
- local_spi=0,
- remote_spi=0,
- crypto_alg=crypto_alg.alg_int_repr,
- local_crypto_key_len=0,
- local_crypto_key=None,
- remote_crypto_key_len=0,
- remote_crypto_key=None,
- integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
- local_integ_key_len=0,
- local_integ_key=None,
- remote_integ_key_len=0,
- remote_integ_key=None,
- tx_table_id=0
+ tunnel=ipip_tunnel
)
- ipsec_tunnels = [None] * existing_tunnels
- ckeys = [bytes()] * existing_tunnels
- ikeys = [bytes()] * existing_tunnels
+ ipip_tunnels = [None] * existing_tunnels
for i in range(existing_tunnels, n_tunnels):
- ckeys.append(
- gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
- )
- if integ_alg:
- ikeys.append(
- gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
- )
- args[u"local_spi"] = spi_d[u"spi_1"] + i
- args[u"remote_spi"] = spi_d[u"spi_2"] + i
- args[u"local_ip"] = IPAddress.create_ip_address_object(
+ args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
tun_ips[u"ip1"] + i * addr_incr
)
- args[u"remote_ip"] = IPAddress.create_ip_address_object(
+ args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
tun_ips[u"ip2"]
)
- args[u"local_crypto_key_len"] = len(ckeys[i])
- args[u"local_crypto_key"] = ckeys[i]
- args[u"remote_crypto_key_len"] = len(ckeys[i])
- args[u"remote_crypto_key"] = ckeys[i]
- if integ_alg:
- args[u"local_integ_key_len"] = len(ikeys[i])
- args[u"local_integ_key"] = ikeys[i]
- args[u"remote_integ_key_len"] = len(ikeys[i])
- args[u"remote_integ_key"] = ikeys[i]
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
- err_msg = f"Failed to add IPsec tunnel interfaces on host" \
+ err_msg = f"Failed to add IPIP tunnel interfaces on host" \
f" {nodes[u'DUT1'][u'host']}"
- ipsec_tunnels.extend(
+ ipip_tunnels.extend(
[
reply[u"sw_if_index"]
for reply in papi_exec.get_replies(err_msg)
if u"sw_if_index" in reply
]
)
+ # Configure IPSec SAD entries
+ ckeys = [bytes()] * existing_tunnels
+ ikeys = [bytes()] * existing_tunnels
+ cmd = u"ipsec_sad_entry_add_del_v2"
+ c_key = dict(
+ length=0,
+ data=None
+ )
+ i_key = dict(
+ length=0,
+ data=None
+ )
+ sad_entry = dict(
+ sad_id=None,
+ spi=None,
+ protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
+ crypto_algorithm=crypto_alg.alg_int_repr,
+ crypto_key=c_key,
+ integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
+ integrity_key=i_key,
+ flags=None,
+ tunnel_src=0,
+ tunnel_dst=0,
+ tunnel_flags=int(
+ TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+ ),
+ dscp=int(IpDscp.IP_API_DSCP_CS0),
+ table_id=0,
+ salt=0,
+ udp_src_port=IPSEC_UDP_PORT_NONE,
+ udp_dst_port=IPSEC_UDP_PORT_NONE
+ )
+ args = dict(
+ is_add=True,
+ entry=sad_entry
+ )
+ for i in range(existing_tunnels, n_tunnels):
+ ckeys.append(
+ gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
+ )
+ ikeys.append(
+ gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
+ )
+ # SAD entry for outband / tx path
+ args[u"entry"][u"sad_id"] = i
+ args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
+
+ args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
+ args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
+ if integ_alg:
+ args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
+ args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
+ args[u"entry"][u"flags"] = int(
+ IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
+ )
+ papi_exec.add(
+ cmd, history=bool(not 1 < i < n_tunnels - 2), **args
+ )
+ # SAD entry for inband / rx path
+ args[u"entry"][u"sad_id"] = 100000 + i
+ args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
+
+ args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
+ args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
+ if integ_alg:
+ args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
+ args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
+ args[u"entry"][u"flags"] = int(
+ IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
+ IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
+ )
+ papi_exec.add(
+ cmd, history=bool(not 1 < i < n_tunnels - 2), **args
+ )
+ err_msg = f"Failed to add IPsec SAD entries on host" \
+ f" {nodes[u'DUT1'][u'host']}"
+ papi_exec.get_replies(err_msg)
+ # Add protection for tunnels with IPSEC
+ cmd = u"ipsec_tunnel_protect_update"
+ n_hop = dict(
+ address=0,
+ via_label=MPLS_LABEL_INVALID,
+ obj_id=Constants.BITWISE_NON_ZERO
+ )
+ ipsec_tunnel_protect = dict(
+ sw_if_index=None,
+ nh=n_hop,
+ sa_out=None,
+ n_sa_in=1,
+ sa_in=None
+ )
+ args = dict(
+ tunnel=ipsec_tunnel_protect
+ )
+ for i in range(existing_tunnels, n_tunnels):
+ args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
+ args[u"tunnel"][u"sa_out"] = i
+ args[u"tunnel"][u"sa_in"] = [100000 + i]
+ papi_exec.add(
+ cmd, history=bool(not 1 < i < n_tunnels - 2), **args
+ )
+ err_msg = f"Failed to add protection for tunnels with IPSEC " \
+ f"on host {nodes[u'DUT1'][u'host']}"
+ papi_exec.get_replies(err_msg)
+
# Configure unnumbered interfaces
cmd = u"sw_interface_set_unnumbered"
args = dict(
unnumbered_sw_if_index=0
)
for i in range(existing_tunnels, n_tunnels):
- args[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
+ args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
)
for i in range(existing_tunnels, n_tunnels):
- args[u"sw_if_index"] = ipsec_tunnels[i]
+ args[u"sw_if_index"] = ipip_tunnels[i]
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
args[u"route"] = IPUtil.compose_vpp_route_structure(
nodes[u"DUT1"], (raddr_ip2 + i).compressed,
prefix_len=128 if raddr_ip2.version == 6 else 32,
- interface=ipsec_tunnels[i]
+ interface=ipip_tunnels[i]
)
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels=0):
"""Create multiple IPsec tunnel interfaces on DUT2 node using PAPI.
+ This method accesses keys generated by DUT1 method
+ and does not return anything.
+
:param nodes: VPP nodes to create tunnel interfaces.
:param tun_ips: Dictionary with VPP node 1 ipsec tunnel interface
IPv4/IPv6 address (ip1) and VPP node 2 ipsec tunnel interface
:type if2_key: str
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type ckeys: list
- :type integ_alg: IntegAlg
- :type ikeys: list
+ :type ckeys: Sequence[bytes]
+ :type integ_alg: Optional[IntegAlg]
+ :type ikeys: Sequence[bytes]
:type addr_incr: int
:type spi_d: dict
:type existing_tunnels: int
err_msg = f"Failed to set IP address on interface {if2_key} " \
f"on host {nodes[u'DUT2'][u'host']}"
papi_exec.add(cmd, **args).get_reply(err_msg)
- # Configure IPsec tunnel interfaces
- cmd = u"ipsec_tunnel_if_add_del"
+ # Configure IPIP tunnel interfaces
+ cmd = u"ipip_add_tunnel"
+ ipip_tunnel = dict(
+ instance=Constants.BITWISE_NON_ZERO,
+ src=None,
+ dst=None,
+ table_id=0,
+ flags=int(
+ TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+ ),
+ mode=int(TunnelMode.TUNNEL_API_MODE_P2P),
+ dscp=int(IpDscp.IP_API_DSCP_CS0)
+ )
args = dict(
- is_add=True,
- local_ip=IPAddress.create_ip_address_object(tun_ips[u"ip2"]),
- remote_ip=None,
- local_spi=0,
- remote_spi=0,
- crypto_alg=crypto_alg.alg_int_repr,
- local_crypto_key_len=0,
- local_crypto_key=None,
- remote_crypto_key_len=0,
- remote_crypto_key=None,
- integ_alg=integ_alg.alg_int_repr if integ_alg else 0,
- local_integ_key_len=0,
- local_integ_key=None,
- remote_integ_key_len=0,
- remote_integ_key=None,
- tx_table_id=0
+ tunnel=ipip_tunnel
)
- ipsec_tunnels = [None] * existing_tunnels
+ ipip_tunnels = [None] * existing_tunnels
for i in range(existing_tunnels, n_tunnels):
- args[u"local_spi"] = spi_d[u"spi_2"] + i
- args[u"remote_spi"] = spi_d[u"spi_1"] + i
- args[u"local_ip"] = IPAddress.create_ip_address_object(
+ args[u"tunnel"][u"src"] = IPAddress.create_ip_address_object(
tun_ips[u"ip2"]
)
- args[u"remote_ip"] = IPAddress.create_ip_address_object(
+ args[u"tunnel"][u"dst"] = IPAddress.create_ip_address_object(
tun_ips[u"ip1"] + i * addr_incr
)
- args[u"local_crypto_key_len"] = len(ckeys[i])
- args[u"local_crypto_key"] = ckeys[i]
- args[u"remote_crypto_key_len"] = len(ckeys[i])
- args[u"remote_crypto_key"] = ckeys[i]
- if integ_alg:
- args[u"local_integ_key_len"] = len(ikeys[i])
- args[u"local_integ_key"] = ikeys[i]
- args[u"remote_integ_key_len"] = len(ikeys[i])
- args[u"remote_integ_key"] = ikeys[i]
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
- err_msg = f"Failed to add IPsec tunnel interfaces " \
- f"on host {nodes[u'DUT2'][u'host']}"
- ipsec_tunnels.extend(
+ err_msg = f"Failed to add IPIP tunnel interfaces on host" \
+ f" {nodes[u'DUT2'][u'host']}"
+ ipip_tunnels.extend(
[
reply[u"sw_if_index"]
for reply in papi_exec.get_replies(err_msg)
if u"sw_if_index" in reply
]
)
+ # Configure IPSec SAD entries
+ cmd = u"ipsec_sad_entry_add_del_v2"
+ c_key = dict(
+ length=0,
+ data=None
+ )
+ i_key = dict(
+ length=0,
+ data=None
+ )
+ sad_entry = dict(
+ sad_id=None,
+ spi=None,
+ protocol=int(IPsecProto.IPSEC_API_PROTO_ESP),
+
+ crypto_algorithm=crypto_alg.alg_int_repr,
+ crypto_key=c_key,
+ integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0,
+ integrity_key=i_key,
+
+ flags=None,
+ tunnel_src=0,
+ tunnel_dst=0,
+ tunnel_flags=int(
+ TunnelEncpaDecapFlags.TUNNEL_API_ENCAP_DECAP_FLAG_NONE
+ ),
+ dscp=int(IpDscp.IP_API_DSCP_CS0),
+ table_id=0,
+ salt=0,
+ udp_src_port=IPSEC_UDP_PORT_NONE,
+ udp_dst_port=IPSEC_UDP_PORT_NONE
+ )
+ args = dict(
+ is_add=True,
+ entry=sad_entry
+ )
+ for i in range(existing_tunnels, n_tunnels):
+ ckeys.append(
+ gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))
+ )
+ ikeys.append(
+ gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))
+ )
+ # SAD entry for outband / tx path
+ args[u"entry"][u"sad_id"] = 100000 + i
+ args[u"entry"][u"spi"] = spi_d[u"spi_2"] + i
+
+ args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
+ args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
+ if integ_alg:
+ args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
+ args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
+ args[u"entry"][u"flags"] = int(
+ IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE
+ )
+ papi_exec.add(
+ cmd, history=bool(not 1 < i < n_tunnels - 2), **args
+ )
+ # SAD entry for inband / rx path
+ args[u"entry"][u"sad_id"] = i
+ args[u"entry"][u"spi"] = spi_d[u"spi_1"] + i
+
+ args[u"entry"][u"crypto_key"][u"length"] = len(ckeys[i])
+ args[u"entry"][u"crypto_key"][u"data"] = ckeys[i]
+ if integ_alg:
+ args[u"entry"][u"integrity_key"][u"length"] = len(ikeys[i])
+ args[u"entry"][u"integrity_key"][u"data"] = ikeys[i]
+ args[u"entry"][u"flags"] = int(
+ IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE |
+ IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_INBOUND
+ )
+ papi_exec.add(
+ cmd, history=bool(not 1 < i < n_tunnels - 2), **args
+ )
+ err_msg = f"Failed to add IPsec SAD entries on host" \
+ f" {nodes[u'DUT2'][u'host']}"
+ papi_exec.get_replies(err_msg)
+ # Add protection for tunnels with IPSEC
+ cmd = u"ipsec_tunnel_protect_update"
+ n_hop = dict(
+ address=0,
+ via_label=MPLS_LABEL_INVALID,
+ obj_id=Constants.BITWISE_NON_ZERO
+ )
+ ipsec_tunnel_protect = dict(
+ sw_if_index=None,
+ nh=n_hop,
+ sa_out=None,
+ n_sa_in=1,
+ sa_in=None
+ )
+ args = dict(
+ tunnel=ipsec_tunnel_protect
+ )
+ for i in range(existing_tunnels, n_tunnels):
+ args[u"tunnel"][u"sw_if_index"] = ipip_tunnels[i]
+ args[u"tunnel"][u"sa_out"] = 100000 + i
+ args[u"tunnel"][u"sa_in"] = [i]
+ papi_exec.add(
+ cmd, history=bool(not 1 < i < n_tunnels - 2), **args
+ )
+ err_msg = f"Failed to add protection for tunnels with IPSEC " \
+ f"on host {nodes[u'DUT2'][u'host']}"
+ papi_exec.get_replies(err_msg)
+
if not existing_tunnels:
# Configure IP route
cmd = u"ip_route_add_del"
unnumbered_sw_if_index=0
)
for i in range(existing_tunnels, n_tunnels):
- args[u"unnumbered_sw_if_index"] = ipsec_tunnels[i]
+ args[u"unnumbered_sw_if_index"] = ipip_tunnels[i]
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
flags=InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
)
for i in range(existing_tunnels, n_tunnels):
- args[u"sw_if_index"] = ipsec_tunnels[i]
+ args[u"sw_if_index"] = ipip_tunnels[i]
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
)
args[u"route"] = IPUtil.compose_vpp_route_structure(
nodes[u"DUT1"], (raddr_ip1 + i).compressed,
prefix_len=128 if raddr_ip1.version == 6 else 32,
- interface=ipsec_tunnels[i]
+ interface=ipip_tunnels[i]
)
papi_exec.add(
cmd, history=bool(not 1 < i < n_tunnels - 2), **args
def vpp_ipsec_create_tunnel_interfaces(
nodes, tun_if1_ip_addr, tun_if2_ip_addr, if1_key, if2_key,
n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range,
- existing_tunnels=0):
+ existing_tunnels=0, return_keys=False):
"""Create multiple IPsec tunnel interfaces between two VPP nodes.
+ Some deployments (e.g. devicetest) need to know the generated keys.
+ But other deployments (e.g. scale perf test) would get spammed
+ if we returned keys every time.
+
:param nodes: VPP nodes to create tunnel interfaces.
:param tun_if1_ip_addr: VPP node 1 ipsec tunnel interface IPv4/IPv6
address.
and to 128 in case of IPv6.
:param existing_tunnels: Number of tunnel interfaces before creation.
Useful mainly for reconf tests. Default 0.
+ :param return_keys: Whether generated keys should be returned.
:type nodes: dict
:type tun_if1_ip_addr: str
:type tun_if2_ip_addr: str
:type if2_key: str
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type integ_alg: IntegAlg
+ :type integ_alg: Optonal[IntegAlg]
:type raddr_ip1: string
:type raddr_ip2: string
:type raddr_range: int
:type existing_tunnels: int
+ :type return_keys: bool
+ :returns: Ckeys, ikeys, spi_1, spi_2.
+ :rtype: Optional[List[bytes], List[bytes], int, int]
"""
n_tunnels = int(n_tunnels)
existing_tunnels = int(existing_tunnels)
nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
)
- if u"DUT2" not in nodes.keys():
- return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
- IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
- nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
- integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
- )
+ if u"DUT2" in nodes.keys():
+ IPsecUtil._ipsec_create_tunnel_interfaces_dut2_vat(
+ nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
+ integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
+ existing_tunnels
+ )
else:
ckeys, ikeys = IPsecUtil._ipsec_create_tunnel_interfaces_dut1_papi(
nodes, tun_ips, if1_key, if2_key, n_tunnels, crypto_alg,
integ_alg, raddr_ip2, addr_incr, spi_d, existing_tunnels
)
- if u"DUT2" not in nodes.keys():
- return ckeys[0], ikeys[0], spi_d[u"spi_1"], spi_d[u"spi_2"]
- IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
- nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
- integ_alg, ikeys, raddr_ip1, addr_incr, spi_d, existing_tunnels
- )
+ if u"DUT2" in nodes.keys():
+ IPsecUtil._ipsec_create_tunnel_interfaces_dut2_papi(
+ nodes, tun_ips, if2_key, n_tunnels, crypto_alg, ckeys,
+ integ_alg, ikeys, raddr_ip1, addr_incr, spi_d,
+ existing_tunnels
+ )
- return None, None, None, None
+ if return_keys:
+ return ckeys, ikeys, spi_d[u"spi_1"], spi_d[u"spi_2"]
+ return None
@staticmethod
def _create_ipsec_script_files(dut, instances):
:type if2_ip_addr: str
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:type raddr_ip1: string
:type raddr_ip2: string
:type raddr_range: int
gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)), u"hex"
)
integ = u""
+ ikey = getattr(
+ gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
+ )
if integ_alg:
- ikey = getattr(
- gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)), u"hex"
- )
integ = (
f"integ-alg {integ_alg.alg_name} "
f"local-integ-key {ikey} "
:type interface2: str or int
:type n_tunnels: int
:type crypto_alg: CryptoAlg
- :type integ_alg: IntegAlg
+ :type integ_alg: Optional[IntegAlg]
:type tunnel_ip1: str
:type tunnel_ip2: str
:type raddr_ip1: string
IPsecUtil.get_integ_alg_key_len(integ_alg)
).decode() if integ_alg else u""
+ rmac = Topology.get_interface_mac(nodes[u"DUT2"], interface2) \
+ if u"DUT2" in nodes.keys() \
+ else Topology.get_interface_mac(nodes[u"TG"], interface2)
IPsecUtil.vpp_ipsec_set_ip_route(
nodes[u"DUT1"], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2,
- interface1, raddr_range)
- IPsecUtil.vpp_ipsec_set_ip_route(
- nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
- interface2, raddr_range)
+ interface1, raddr_range, rmac)
IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT1"], spd_id)
IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT1"], spd_id, interface1)
proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
)
- IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
- IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
- IPsecUtil.vpp_ipsec_policy_add(
- nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=False,
- proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
- )
- IPsecUtil.vpp_ipsec_policy_add(
- nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS, inbound=True,
- proto=50, laddr_range=u"100.0.0.0/8", raddr_range=u"100.0.0.0/8"
- )
-
IPsecUtil.vpp_ipsec_add_sad_entries(
nodes[u"DUT1"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
integ_alg, integ_key, tunnel_ip1, tunnel_ip2
)
IPsecUtil.vpp_ipsec_add_sad_entries(
- nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key,
- integ_alg, integ_key, tunnel_ip1, tunnel_ip2
+ nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
+ integ_alg, integ_key, tunnel_ip2, tunnel_ip1
)
IPsecUtil.vpp_ipsec_spd_add_entries(
- nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2
+ nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
)
- IPsecUtil.vpp_ipsec_add_sad_entries(
- nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
- integ_alg, integ_key, tunnel_ip2, tunnel_ip1
- )
+ if u"DUT2" in nodes.keys():
+ IPsecUtil.vpp_ipsec_set_ip_route(
+ nodes[u"DUT2"], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1,
+ interface2, raddr_range)
+
+ IPsecUtil.vpp_ipsec_add_spd(nodes[u"DUT2"], spd_id)
+ IPsecUtil.vpp_ipsec_spd_add_if(nodes[u"DUT2"], spd_id, interface2)
+ IPsecUtil.vpp_ipsec_policy_add(
+ nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
+ inbound=False, proto=50, laddr_range=u"100.0.0.0/8",
+ raddr_range=u"100.0.0.0/8"
+ )
+ IPsecUtil.vpp_ipsec_policy_add(
+ nodes[u"DUT2"], spd_id, p_hi, PolicyAction.BYPASS,
+ inbound=True, proto=50, laddr_range=u"100.0.0.0/8",
+ raddr_range=u"100.0.0.0/8"
+ )
- IPsecUtil.vpp_ipsec_spd_add_entries(
- nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1
- )
+ IPsecUtil.vpp_ipsec_add_sad_entries(
+ nodes[u"DUT2"], n_tunnels, sa_id_1, spi_1, crypto_alg,
+ crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2
+ )
+ IPsecUtil.vpp_ipsec_spd_add_entries(
+ nodes[u"DUT2"], n_tunnels, spd_id, p_lo, True, sa_id_1,
+ raddr_ip2
+ )
- IPsecUtil.vpp_ipsec_add_sad_entries(
- nodes[u"DUT1"], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key,
- integ_alg, integ_key, tunnel_ip2, tunnel_ip1
- )
+ IPsecUtil.vpp_ipsec_add_sad_entries(
+ nodes[u"DUT2"], n_tunnels, sa_id_2, spi_2, crypto_alg,
+ crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1
+ )
+ IPsecUtil.vpp_ipsec_spd_add_entries(
+ nodes[u"DUT2"], n_tunnels, spd_id, p_lo, False, sa_id_2,
+ raddr_ip1
+ )
- IPsecUtil.vpp_ipsec_spd_add_entries(
- nodes[u"DUT1"], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1
- )
@staticmethod
def vpp_ipsec_show(node):
:type node: dict
"""
PapiSocketExecutor.run_cli_cmd(node, u"show ipsec")
+
+ @staticmethod
+ def show_ipsec_security_association(node):
+ """Show IPSec security association.
+
+ :param node: DUT node.
+ :type node: dict
+ """
+ cmds = [
+ u"ipsec_sa_v2_dump"
+ ]
+ PapiSocketExecutor.dump_and_log(node, cmds)