diff --git a/rust/src/lib/iface.rs b/rust/src/lib/iface.rs index a62ec06191..6af1e407d9 100644 --- a/rust/src/lib/iface.rs +++ b/rust/src/lib/iface.rs @@ -10,7 +10,7 @@ use crate::{ InfiniBandInterface, IpsecInterface, LinuxBridgeInterface, LoopbackInterface, MacSecInterface, MacVlanInterface, MacVtapInterface, NmstateError, OvsBridgeInterface, OvsInterface, VlanInterface, - VrfInterface, VxlanInterface, + VrfInterface, VxlanInterface, XfrmInterface, }; use crate::state::merge_json_value; @@ -69,6 +69,8 @@ pub enum InterfaceType { MacSec, /// Ipsec connection. Ipsec, + /// Linux Xfrm kernel interface + Xfrm, /// Unknown interface. Unknown, /// Reserved for future use. @@ -102,6 +104,7 @@ impl From<&str> for InterfaceType { "macsec" => InterfaceType::MacSec, "ipsec" => InterfaceType::Ipsec, "unknown" => InterfaceType::Unknown, + "xfrm" => InterfaceType::Xfrm, _ => InterfaceType::Other(s.to_string()), } } @@ -131,6 +134,7 @@ impl std::fmt::Display for InterfaceType { InterfaceType::Tun => "tun", InterfaceType::MacSec => "macsec", InterfaceType::Ipsec => "ipsec", + InterfaceType::Xfrm => "xfrm", InterfaceType::Other(ref s) => s, } ) @@ -309,6 +313,8 @@ pub enum Interface { MacSec(MacSecInterface), /// Ipsec connection Ipsec(IpsecInterface), + /// Linux xfrm interface + Xfrm(XfrmInterface), } impl<'de> Deserialize<'de> for Interface { @@ -420,6 +426,11 @@ impl<'de> Deserialize<'de> for Interface { .map_err(serde::de::Error::custom)?; Ok(Interface::Ipsec(inner)) } + Some(InterfaceType::Xfrm) => { + let inner = XfrmInterface::deserialize(v) + .map_err(serde::de::Error::custom)?; + Ok(Interface::Xfrm(inner)) + } Some(iface_type) => { log::warn!("Unsupported interface type {}", iface_type); let inner = UnknownInterface::deserialize(v) @@ -535,6 +546,11 @@ impl Interface { new_iface.base = iface.base.clone_name_type_only(); Self::Ipsec(new_iface) } + Self::Xfrm(iface) => { + let mut new_iface = XfrmInterface::new(); + new_iface.base = iface.base.clone_name_type_only(); + Self::Xfrm(new_iface) + } Self::Unknown(iface) => { let mut new_iface = UnknownInterface::new(); new_iface.base = iface.base.clone_name_type_only(); @@ -631,6 +647,7 @@ impl Interface { Self::Loopback(iface) => &iface.base, Self::MacSec(iface) => &iface.base, Self::Ipsec(iface) => &iface.base, + Self::Xfrm(iface) => &iface.base, Self::Unknown(iface) => &iface.base, } } @@ -652,6 +669,7 @@ impl Interface { Self::Loopback(iface) => &mut iface.base, Self::MacSec(iface) => &mut iface.base, Self::Ipsec(iface) => &mut iface.base, + Self::Xfrm(iface) => &mut iface.base, Self::Unknown(iface) => &mut iface.base, } } diff --git a/rust/src/lib/ifaces/ipsec.rs b/rust/src/lib/ifaces/ipsec.rs index b37205a256..ab42518db4 100644 --- a/rust/src/lib/ifaces/ipsec.rs +++ b/rust/src/lib/ifaces/ipsec.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use crate::{BaseInterface, InterfaceType, NetworkState}; @@ -89,6 +89,29 @@ pub struct LibreswanConfig { pub ike: Option, #[serde(skip_serializing_if = "Option::is_none")] pub esp: Option, + #[serde( + skip_serializing_if = "Option::is_none", + default, + deserialize_with = "crate::deserializer::option_u64_or_string" + )] + pub dpddelay: Option, + #[serde( + skip_serializing_if = "Option::is_none", + default, + deserialize_with = "crate::deserializer::option_u64_or_string" + )] + pub dpdtimeout: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub dpdaction: Option, + #[serde( + skip_serializing_if = "Option::is_none", + rename = "ipsec-interface", + default, + deserialize_with = "parse_ipsec_iface" + )] + pub ipsec_interface: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub authby: Option, } impl LibreswanConfig { @@ -96,3 +119,49 @@ impl LibreswanConfig { Self::default() } } + +fn parse_ipsec_iface<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let v = serde_json::Value::deserialize(deserializer)?; + + match v { + serde_json::Value::Bool(b) => { + if b { + Ok(Some("yes".to_string())) + } else { + Ok(Some("no".to_string())) + } + } + serde_json::Value::Number(d) => { + if let Some(d) = d.as_u64() { + Ok(Some(d.to_string())) + } else { + Err(serde::de::Error::custom( + "Invalid ipsec-interface value, should be \ + unsigned integer or yes or no", + )) + } + } + serde_json::Value::String(s) => match s.as_str() { + "yes" | "no" => Ok(Some(s)), + _ => { + if s.parse::().is_ok() { + Ok(Some(s)) + } else { + Err(serde::de::Error::custom( + "Invalid ipsec-interface value, should be \ + unsigned integer or yes or no", + )) + } + } + }, + _ => Err(serde::de::Error::custom( + "Invalid ipsec-interface value, should be \ + unsigned integer or yes or no", + )), + } +} diff --git a/rust/src/lib/ifaces/mod.rs b/rust/src/lib/ifaces/mod.rs index b4c657d396..59da76e666 100644 --- a/rust/src/lib/ifaces/mod.rs +++ b/rust/src/lib/ifaces/mod.rs @@ -9,6 +9,7 @@ mod ipsec; mod loopback; mod vrf; mod vxlan; +mod xfrm; // The pub(crate) is only for unit test mod infiniband; pub(crate) mod inter_ifaces_controller; @@ -20,42 +21,45 @@ mod ovs; mod sriov; mod vlan; -pub use base::*; -pub use bond::{ +pub use self::base::*; +pub use self::bond::{ BondAdSelect, BondAllPortsActive, BondArpAllTargets, BondArpValidate, BondConfig, BondFailOverMac, BondInterface, BondLacpRate, BondMode, BondOptions, BondPortConfig, BondPrimaryReselect, BondXmitHashPolicy, }; -pub use bridge_vlan::{ +pub use self::bridge_vlan::{ BridgePortTrunkTag, BridgePortVlanConfig, BridgePortVlanMode, BridgePortVlanRange, }; -pub use dummy::DummyInterface; -pub use ethernet::{ +pub use self::dummy::DummyInterface; +pub use self::ethernet::{ EthernetConfig, EthernetDuplex, EthernetInterface, VethConfig, }; -pub use ethtool::{ +pub use self::ethtool::{ EthtoolCoalesceConfig, EthtoolConfig, EthtoolFeatureConfig, EthtoolPauseConfig, EthtoolRingConfig, }; -pub use infiniband::{InfiniBandConfig, InfiniBandInterface, InfiniBandMode}; -pub(crate) use inter_ifaces::MergedInterfaces; -pub use inter_ifaces::*; -pub use ipsec::{IpsecInterface, LibreswanConfig}; -pub use linux_bridge::{ +pub use self::infiniband::{ + InfiniBandConfig, InfiniBandInterface, InfiniBandMode, +}; +pub(crate) use self::inter_ifaces::MergedInterfaces; +pub use self::inter_ifaces::*; +pub use self::ipsec::{IpsecInterface, LibreswanConfig}; +pub use self::linux_bridge::{ LinuxBridgeConfig, LinuxBridgeInterface, LinuxBridgeMulticastRouterType, LinuxBridgeOptions, LinuxBridgePortConfig, LinuxBridgeStpOptions, }; -pub use loopback::LoopbackInterface; -pub use mac_vlan::{MacVlanConfig, MacVlanInterface, MacVlanMode}; -pub use mac_vtap::{MacVtapConfig, MacVtapInterface, MacVtapMode}; -pub use macsec::{MacSecConfig, MacSecInterface, MacSecValidate}; -pub use ovs::{ +pub use self::loopback::LoopbackInterface; +pub use self::mac_vlan::{MacVlanConfig, MacVlanInterface, MacVlanMode}; +pub use self::mac_vtap::{MacVtapConfig, MacVtapInterface, MacVtapMode}; +pub use self::macsec::{MacSecConfig, MacSecInterface, MacSecValidate}; +pub use self::ovs::{ OvsBridgeBondConfig, OvsBridgeBondMode, OvsBridgeBondPortConfig, OvsBridgeConfig, OvsBridgeInterface, OvsBridgeOptions, OvsBridgePortConfig, OvsBridgeStpOptions, OvsDpdkConfig, OvsInterface, OvsPatchConfig, }; -pub use sriov::{SrIovConfig, SrIovVfConfig}; -pub use vlan::{VlanConfig, VlanInterface, VlanProtocol}; -pub use vrf::{VrfConfig, VrfInterface}; -pub use vxlan::{VxlanConfig, VxlanInterface}; +pub use self::sriov::{SrIovConfig, SrIovVfConfig}; +pub use self::vlan::{VlanConfig, VlanInterface, VlanProtocol}; +pub use self::vrf::{VrfConfig, VrfInterface}; +pub use self::vxlan::{VxlanConfig, VxlanInterface}; +pub use self::xfrm::XfrmInterface; diff --git a/rust/src/lib/ifaces/xfrm.rs b/rust/src/lib/ifaces/xfrm.rs new file mode 100644 index 0000000000..e26905f1c2 --- /dev/null +++ b/rust/src/lib/ifaces/xfrm.rs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +use crate::{BaseInterface, InterfaceType}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct XfrmInterface { + #[serde(flatten)] + pub base: BaseInterface, +} + +impl Default for XfrmInterface { + fn default() -> Self { + let mut base = BaseInterface::new(); + base.iface_type = InterfaceType::Xfrm; + Self { base } + } +} + +impl XfrmInterface { + pub fn new() -> Self { + Self::default() + } +} diff --git a/rust/src/lib/lib.rs b/rust/src/lib/lib.rs index ac3eb8437d..2378a21cae 100644 --- a/rust/src/lib/lib.rs +++ b/rust/src/lib/lib.rs @@ -151,6 +151,7 @@ pub use crate::ifaces::{ OvsBridgeStpOptions, OvsDpdkConfig, OvsInterface, OvsPatchConfig, SrIovConfig, SrIovVfConfig, VethConfig, VlanConfig, VlanInterface, VlanProtocol, VrfConfig, VrfInterface, VxlanConfig, VxlanInterface, + XfrmInterface, }; pub use crate::ip::{ AddressFamily, Dhcpv4ClientId, Dhcpv6Duid, InterfaceIpAddr, InterfaceIpv4, diff --git a/rust/src/lib/nispor/base_iface.rs b/rust/src/lib/nispor/base_iface.rs index 5a11890bb6..415769d3ef 100644 --- a/rust/src/lib/nispor/base_iface.rs +++ b/rust/src/lib/nispor/base_iface.rs @@ -26,6 +26,7 @@ fn np_iface_type_to_nmstate( nispor::IfaceType::Vxlan => InterfaceType::Vxlan, nispor::IfaceType::Ipoib => InterfaceType::InfiniBand, nispor::IfaceType::Tun => InterfaceType::Tun, + nispor::IfaceType::Other(v) if v == "xfrm" => InterfaceType::Xfrm, _ => InterfaceType::Other(format!("{np_iface_type:?}")), } } diff --git a/rust/src/lib/nispor/show.rs b/rust/src/lib/nispor/show.rs index 99a8c15d63..e1a5ffb9fe 100644 --- a/rust/src/lib/nispor/show.rs +++ b/rust/src/lib/nispor/show.rs @@ -21,7 +21,7 @@ use crate::{ vxlan::np_vxlan_to_nmstate, }, DummyInterface, Interface, InterfaceType, Interfaces, LoopbackInterface, - NetworkState, NmstateError, OvsInterface, UnknownInterface, + NetworkState, NmstateError, OvsInterface, UnknownInterface, XfrmInterface, }; pub(crate) fn nispor_retrieve( @@ -130,6 +130,11 @@ pub(crate) fn nispor_retrieve( InterfaceType::MacSec => { Interface::MacSec(np_macsec_to_nmstate(np_iface, base_iface)) } + InterfaceType::Xfrm => { + let mut iface = XfrmInterface::new(); + iface.base = base_iface; + Interface::Xfrm(iface) + } _ => { log::info!( "Got unsupported interface {} type {:?}", diff --git a/rust/src/lib/nm/query_apply/vpn.rs b/rust/src/lib/nm/query_apply/vpn.rs index cc17474ff4..f8a3e2b1e4 100644 --- a/rust/src/lib/nm/query_apply/vpn.rs +++ b/rust/src/lib/nm/query_apply/vpn.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::collections::HashMap; +use std::str::FromStr; use crate::{ Interface, InterfaceType, IpsecInterface, LibreswanConfig, NmstateError, @@ -59,6 +60,12 @@ fn get_libreswan_conf(nm_set_vpn: &NmSettingVpn) -> LibreswanConfig { ret.salifetime = data.get("salifetime").cloned(); ret.ike = data.get("ike").cloned(); ret.esp = data.get("esp").cloned(); + ret.dpddelay = data.get("dpddelay").and_then(|d| u64::from_str(d).ok()); + ret.dpdtimeout = + data.get("dpdtimeout").and_then(|d| u64::from_str(d).ok()); + ret.dpdaction = data.get("dpdaction").cloned(); + ret.ipsec_interface = data.get("ipsec-interface").cloned(); + ret.authby = data.get("authby").cloned(); } if let Some(secrets) = nm_set_vpn.secrets.as_ref() { ret.psk = secrets.get("pskvalue").cloned(); diff --git a/rust/src/lib/nm/settings/vpn.rs b/rust/src/lib/nm/settings/vpn.rs index a603316705..0bc8ca489a 100644 --- a/rust/src/lib/nm/settings/vpn.rs +++ b/rust/src/lib/nm/settings/vpn.rs @@ -45,6 +45,21 @@ pub(crate) fn gen_nm_ipsec_vpn_setting( if let Some(v) = conf.esp.as_deref() { vpn_data.insert("esp".into(), v.to_string()); } + if let Some(v) = conf.dpddelay { + vpn_data.insert("dpddelay".into(), v.to_string()); + } + if let Some(v) = conf.dpdtimeout { + vpn_data.insert("dpdtimeout".into(), v.to_string()); + } + if let Some(v) = conf.dpdaction.as_deref() { + vpn_data.insert("dpdaction".into(), v.to_string()); + } + if let Some(v) = conf.ipsec_interface.as_deref() { + vpn_data.insert("ipsec-interface".into(), v.to_string()); + } + if let Some(v) = conf.authby.as_deref() { + vpn_data.insert("authby".into(), v.to_string()); + } let mut nm_vpn_set = NmSettingVpn::default(); nm_vpn_set.data = Some(vpn_data); diff --git a/rust/src/lib/query_apply/iface.rs b/rust/src/lib/query_apply/iface.rs index f1b8d9540f..9ad6c6d8dc 100644 --- a/rust/src/lib/query_apply/iface.rs +++ b/rust/src/lib/query_apply/iface.rs @@ -234,7 +234,7 @@ impl Interface { } impl InterfaceType { - pub(crate) const SUPPORTED_LIST: [InterfaceType; 16] = [ + pub(crate) const SUPPORTED_LIST: [InterfaceType; 17] = [ InterfaceType::Bond, InterfaceType::LinuxBridge, InterfaceType::Dummy, @@ -251,5 +251,6 @@ impl InterfaceType { InterfaceType::MacSec, InterfaceType::Vrf, InterfaceType::Ipsec, + InterfaceType::Xfrm, ]; } diff --git a/tests/integration/ipsec_test.py b/tests/integration/ipsec_test.py index 8a7d8bb03e..92ad741935 100644 --- a/tests/integration/ipsec_test.py +++ b/tests/integration/ipsec_test.py @@ -26,30 +26,31 @@ CA_NAME = "nmstate-test-ca.example.org" HOSTA_NAME = "hosta.example.org" HOSTA_NIC = "hosta_nic" -HOSTA_IPV4 = "192.0.2.251" +HOSTA_IPV4_CRT = "192.0.2.251" HOSTA_IPV4_PSK = "192.0.2.250" HOSTA_IPV4_RSA = "192.0.2.249" HOSTA_IPSEC_CONN_NAME = "hosta_conn" HOSTB_NAME = "hostb.example.org" HOSTB_NIC = "hostb_nic" -HOSTB_IPV4 = "192.0.2.252" -HOSTB_IPV4_PSK = "192.0.2.253" -HOSTB_IPV4_RSA = "192.0.2.254" +HOSTB_IPV4_CRT = "192.0.2.152" +HOSTB_IPV4_PSK = "192.0.2.153" +HOSTB_IPV4_RSA = "192.0.2.154" HOSTB_VPN_SUBNET_PREFIX = "203.0.113" HOSTB_VPN_SUBNET = f"{HOSTB_VPN_SUBNET_PREFIX}.0/24" HOSTB_EXT_IP = "198.51.100.1" HOSTB_DUMMY_NIC = "dummy0" HOSTB_NS = "nmstate_ipsec_test" -HOSTB_IPSEC_CONN_NAME = "hostb_conn" +HOSTB_IPSEC_CONF_NAME = "hostb_conn" +HOSTB_IPSEC_CRT_CONN_NAME = "hostb_conn_crt" HOSTB_IPSEC_PSK_CONN_NAME = "hostb_conn_psk" HOSTB_IPSEC_RSA_CONN_NAME = "hostb_conn_rsa" HOSTB_IPSEC_CONN_CONTENT = """ config setup protostack=netkey -conn hostb_conn +conn hostb_conn_crt hostaddrfamily=ipv4 - left=192.0.2.252 + left=192.0.2.152 leftid=@hostb.example.org leftcert=hostb.example.org leftsubnet=0.0.0.0/0 @@ -64,7 +65,7 @@ conn hostb_conn_psk hostaddrfamily=ipv4 - left=192.0.2.253 + left=192.0.2.153 leftid=@hostb-psk.example.org leftsubnet=0.0.0.0/0 rightaddresspool=203.0.113.102-203.0.113.200 @@ -76,7 +77,7 @@ conn hostb_conn_rsa hostaddrfamily=ipv4 - left=192.0.2.254 + left=192.0.2.154 leftid=@hostb-rsa.example.org leftrsasigkey={RSA_SIGNATURE_HOSTB} leftsubnet=0.0.0.0/0 @@ -135,7 +136,7 @@ def setup_hostb_ipsec_conn(): _import_certs(HOSTB_IPSEC_NSS_DIR) conn_conf_file_path = ( - f"{HOSTB_IPSEC_CONF_DIR}/ipsec.d/{HOSTB_IPSEC_CONN_NAME}.conf" + f"{HOSTB_IPSEC_CONF_DIR}/ipsec.d/{HOSTB_IPSEC_CONF_NAME}.conf" ) with open(conn_conf_file_path, "w") as fd: fd.write( @@ -164,26 +165,22 @@ def setup_hostb_ipsec_conn(): f"ip addr add {HOSTB_EXT_IP}/32 dev {HOSTB_DUMMY_NIC}".split(), check=True, ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ip addr add {HOSTB_IPV4}/24 dev {HOSTB_NIC}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ip addr add {HOSTB_IPV4_PSK}/24 dev {HOSTB_NIC}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ip addr add {HOSTB_IPV4_RSA}/24 dev {HOSTB_NIC}".split(), - check=True, - ) cmdlib.exec_cmd( f"ip netns exec {HOSTB_NS} " f"ip route add {HOSTB_VPN_SUBNET} dev {HOSTB_NIC}".split(), check=True, ) + + for ip in [ + HOSTB_IPV4_CRT, + HOSTB_IPV4_PSK, + HOSTB_IPV4_RSA, + ]: + cmdlib.exec_cmd( + f"ip netns exec {HOSTB_NS} " + f"ip addr add {ip}/24 dev {HOSTB_NIC}".split(), + check=True, + ) cmdlib.exec_cmd( f"ip netns exec {HOSTB_NS} ipsec pluto " f"--config {HOSTB_IPSEC_CONF_DIR}/ipsec.conf " @@ -193,45 +190,24 @@ def setup_hostb_ipsec_conn(): f"--rundir {HOSTB_IPSEC_RUN_DIR}".split(), check=True, ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " - f"--config {conn_conf_file_path} " - f"--add {HOSTB_IPSEC_CONN_NAME}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " - f"--config {conn_conf_file_path} " - f"--add {HOSTB_IPSEC_PSK_CONN_NAME}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " - f"--config {conn_conf_file_path} " - f"--add {HOSTB_IPSEC_RSA_CONN_NAME}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " - f"--asynchronous --up {HOSTB_IPSEC_CONN_NAME}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " - f"--asynchronous --up {HOSTB_IPSEC_PSK_CONN_NAME}".split(), - check=True, - ) - cmdlib.exec_cmd( - f"ip netns exec {HOSTB_NS} " - f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " - f"--asynchronous --up {HOSTB_IPSEC_RSA_CONN_NAME}".split(), - check=True, - ) + for conn_name in [ + HOSTB_IPSEC_CRT_CONN_NAME, + HOSTB_IPSEC_RSA_CONN_NAME, + HOSTB_IPSEC_PSK_CONN_NAME, + ]: + cmdlib.exec_cmd( + f"ip netns exec {HOSTB_NS} " + f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " + f"--config {conn_conf_file_path} " + f"--add {conn_name}".split(), + check=True, + ) + cmdlib.exec_cmd( + f"ip netns exec {HOSTB_NS} " + f"ipsec auto --ctlsocket {HOSTB_IPSEC_RUN_DIR}/pluto.ctl " + f"--asynchronous --up {conn_name}".split(), + check=True, + ) yield finally: _clean_hostb() @@ -349,7 +325,7 @@ def setup_hosta_ip(): { Route.NEXT_HOP_INTERFACE: HOSTA_NIC, Route.DESTINATION: "0.0.0.0/0", - Route.NEXT_HOP_ADDRESS: HOSTB_IPV4, + Route.NEXT_HOP_ADDRESS: HOSTB_IPV4_CRT, }, ] }, @@ -362,7 +338,7 @@ def setup_hosta_ip(): InterfaceIPv4.ENABLED: True, InterfaceIPv4.ADDRESS: [ { - InterfaceIPv4.ADDRESS_IP: HOSTA_IPV4, + InterfaceIPv4.ADDRESS_IP: HOSTA_IPV4_CRT, InterfaceIPv4.ADDRESS_PREFIX_LENGTH: 24, }, { @@ -408,10 +384,15 @@ def _check_ipsec(left, right): def _check_ipsec_ip(ip_net_prefix, nic): - iface_state = show_only([nic])[Interface.KEY][0] - for ip in iface_state.get(Interface.IPV4, {}).get(InterfaceIP.ADDRESS, []): - if ip.get(InterfaceIP.ADDRESS_IP, "").startswith(ip_net_prefix): - return True + try: + iface_state = show_only([nic])[Interface.KEY][0] + for ip in iface_state.get(Interface.IPV4, {}).get( + InterfaceIP.ADDRESS, [] + ): + if ip.get(InterfaceIP.ADDRESS_IP, "").startswith(ip_net_prefix): + return True + except Exception: + pass return False @@ -433,7 +414,7 @@ def test_ipsec_ipv4_libreswan_cert_auth_add_and_remove( ipsec_hosta_conn_cleanup, ): desired_state = yaml.load( - """--- + f"""--- interfaces: - name: hosta_conn type: ipsec @@ -441,10 +422,10 @@ def test_ipsec_ipv4_libreswan_cert_auth_add_and_remove( enabled: true dhcp: true libreswan: - left: 192.0.2.251 + left: {HOSTA_IPV4_CRT} leftid: '%fromcert' leftcert: hosta.example.org - right: 192.0.2.252 + right: {HOSTB_IPV4_CRT} rightid: 'hostb.example.org' ikev2: insist ikelifetime: 24h @@ -453,7 +434,7 @@ def test_ipsec_ipv4_libreswan_cert_auth_add_and_remove( ) libnmstate.apply(desired_state) assert retry_till_true_or_timeout( - RETRY_COUNT, _check_ipsec, HOSTA_IPV4, HOSTB_IPV4 + RETRY_COUNT, _check_ipsec, HOSTA_IPV4_CRT, HOSTB_IPV4_CRT ) assert retry_till_true_or_timeout( RETRY_COUNT, _check_ipsec_ip, HOSTB_VPN_SUBNET_PREFIX, HOSTA_NIC @@ -473,9 +454,9 @@ def test_ipsec_ipv4_libreswan_psk_auth_add_and_remove( dhcp: true libreswan: psk: {PSK} - left: 192.0.2.250 + left: {HOSTA_IPV4_PSK} leftid: 'hosta-psk.example.org' - right: 192.0.2.253 + right: {HOSTB_IPV4_PSK} rightid: 'hostb-psk.example.org' ikev2: insist""", Loader=yaml.SafeLoader, @@ -500,9 +481,9 @@ def test_ipsec_apply_with_hiden_psk(ipsec_hosta_conn_cleanup): dhcp: true libreswan: psk: {PSK} - left: 192.0.2.250 + left: {HOSTA_IPV4_PSK} leftid: 'hosta-psk.example.org' - right: 192.0.2.253 + right: {HOSTB_IPV4_PSK} rightid: 'hostb-psk.example.org' ikev2: insist""", Loader=yaml.SafeLoader, @@ -510,7 +491,7 @@ def test_ipsec_apply_with_hiden_psk(ipsec_hosta_conn_cleanup): libnmstate.apply(desired_state) desired_state = yaml.load( - """--- + f"""--- interfaces: - name: hosta_conn type: ipsec @@ -519,9 +500,9 @@ def test_ipsec_apply_with_hiden_psk(ipsec_hosta_conn_cleanup): dhcp: true libreswan: psk: <_password_hid_by_nmstate> - left: 192.0.2.250 + left: {HOSTA_IPV4_PSK} leftid: 'hosta-psk.example.org' - right: 192.0.2.253 + right: {HOSTB_IPV4_PSK} rightid: 'hostb-psk.example.org' ikev2: insist""", Loader=yaml.SafeLoader, @@ -555,7 +536,7 @@ def test_ipsec_rsa_authenticate(ipsec_hosta_conn_cleanup): ikev2: insist""", Loader=yaml.SafeLoader, ) - libnmstate.apply(desired_state, verify_change=False) + libnmstate.apply(desired_state) assert retry_till_true_or_timeout( RETRY_COUNT, _check_ipsec, HOSTA_IPV4_RSA, HOSTB_IPV4_RSA ) @@ -568,7 +549,7 @@ def test_ipsec_ipv4_libreswan_fromcert( ipsec_hosta_conn_cleanup, ): desired_state = yaml.load( - """--- + f"""--- interfaces: - name: hosta_conn type: ipsec @@ -576,11 +557,11 @@ def test_ipsec_ipv4_libreswan_fromcert( enabled: true dhcp: true libreswan: - left: 192.0.2.251 + left: {HOSTA_IPV4_CRT} leftid: '%fromcert' leftcert: hosta.example.org leftrsasigkey: '%cert' - right: 192.0.2.252 + right: {HOSTB_IPV4_CRT} rightid: '%fromcert' ikev2: insist ikelifetime: 24h @@ -589,8 +570,111 @@ def test_ipsec_ipv4_libreswan_fromcert( ) libnmstate.apply(desired_state) assert retry_till_true_or_timeout( - RETRY_COUNT, _check_ipsec, HOSTA_IPV4, HOSTB_IPV4 + RETRY_COUNT, _check_ipsec, HOSTA_IPV4_CRT, HOSTB_IPV4_CRT ) assert retry_till_true_or_timeout( RETRY_COUNT, _check_ipsec_ip, HOSTB_VPN_SUBNET_PREFIX, HOSTA_NIC ) + + +@pytest.mark.xfail( + reason="NetworkManager-libreswan might be too old", +) +def test_ipsec_ipv4_libreswan_psk_auth_with_ipsec_iface( + ipsec_hosta_conn_cleanup, +): + desired_state = yaml.load( + f"""--- + interfaces: + - name: hosta_conn + type: ipsec + ipv4: + enabled: true + dhcp: true + libreswan: + psk: {PSK} + left: {HOSTA_IPV4_PSK} + leftid: 'hosta-psk.example.org' + right: {HOSTB_IPV4_PSK} + rightid: 'hostb-psk.example.org' + ipsec-interface: 9 + ikev2: insist""", + Loader=yaml.SafeLoader, + ) + libnmstate.apply(desired_state) + assert retry_till_true_or_timeout( + RETRY_COUNT, _check_ipsec, HOSTA_IPV4_PSK, HOSTB_IPV4_PSK + ) + assert retry_till_true_or_timeout( + RETRY_COUNT, _check_ipsec_ip, HOSTB_VPN_SUBNET_PREFIX, "ipsec9" + ) + + +@pytest.mark.xfail( + reason="NetworkManager-libreswan might be too old", +) +def test_ipsec_ipv4_libreswan_psk_auth_with_dpd( + ipsec_hosta_conn_cleanup, +): + desired_state = yaml.load( + f"""--- + interfaces: + - name: hosta_conn + type: ipsec + ipv4: + enabled: true + dhcp: true + libreswan: + psk: {PSK} + left: {HOSTA_IPV4_PSK} + leftid: 'hosta-psk.example.org' + right: {HOSTB_IPV4_PSK} + rightid: 'hostb-psk.example.org' + dpddelay: 1 + dpdtimeout: 60 + dpdaction: restart + ipsec-interface: "10" + ikev2: insist""", + Loader=yaml.SafeLoader, + ) + libnmstate.apply(desired_state) + assert retry_till_true_or_timeout( + RETRY_COUNT, _check_ipsec, HOSTA_IPV4_PSK, HOSTB_IPV4_PSK + ) + assert retry_till_true_or_timeout( + RETRY_COUNT, _check_ipsec_ip, HOSTB_VPN_SUBNET_PREFIX, "ipsec10" + ) + + +@pytest.mark.xfail( + reason="NetworkManager-libreswan might be too old", +) +def test_ipsec_ipv4_libreswan_authby( + ipsec_hosta_conn_cleanup, +): + desired_state = yaml.load( + f"""--- + interfaces: + - name: hosta_conn + type: ipsec + ipv4: + enabled: true + dhcp: true + libreswan: + psk: {PSK} + left: {HOSTA_IPV4_PSK} + leftid: 'hosta-psk.example.org' + right: {HOSTB_IPV4_PSK} + rightid: 'hostb-psk.example.org' + ipsec-interface: 77 + authby: secret + ikev2: insist""", + Loader=yaml.SafeLoader, + ) + libnmstate.apply(desired_state) + assert retry_till_true_or_timeout( + RETRY_COUNT, _check_ipsec, HOSTA_IPV4_PSK, HOSTB_IPV4_PSK + ) + assert retry_till_true_or_timeout( + RETRY_COUNT, _check_ipsec_ip, HOSTB_VPN_SUBNET_PREFIX, "ipsec77" + )