use std::{collections::HashMap, convert::TryFrom};
use log::warn;
use serde::{Deserialize, Serialize};
use zvariant::{Signature, Type};
use super::super::{
NmError, NmIfaceType,
connection::{
bond::{NmSettingBond, NmSettingBondPort},
bridge::{NmSettingBridge, NmSettingBridgePort},
ethtool::NmSettingEthtool,
hsr::NmSettingHsr,
ieee8021x::NmSetting8021X,
iface_match::NmSettingMatch,
infiniband::NmSettingInfiniBand,
ip::NmSettingIp,
ip_tunnel::NmSettingIpTunnel,
ipvlan::NmSettingIpVlan,
loopback::NmSettingLoopback,
mac_vlan::NmSettingMacVlan,
macsec::NmSettingMacSec,
ovs::{
NmSettingOvsBridge, NmSettingOvsDpdk, NmSettingOvsExtIds,
NmSettingOvsIface, NmSettingOvsOtherConfig, NmSettingOvsPatch,
NmSettingOvsPort,
},
sriov::NmSettingSriov,
user::NmSettingUser,
veth::NmSettingVeth,
vlan::NmSettingVlan,
vpn::NmSettingVpn,
vrf::NmSettingVrf,
vxlan::NmSettingVxlan,
wired::NmSettingWired,
},
convert::ToDbusValue,
};
const NM_AUTOCONENCT_PORT_DEFAULT: i32 = -1;
const NM_AUTOCONENCT_PORT_YES: i32 = 1;
const NM_AUTOCONENCT_PORT_NO: i32 = 0;
pub(crate) type NmConnectionDbusOwnedValue =
HashMap<String, HashMap<String, zvariant::OwnedValue>>;
pub(crate) type DbusDictionary = HashMap<String, zvariant::OwnedValue>;
pub(crate) const DBUS_ASV_SIGNATURE: &Signature = &Signature::Dict {
key: zvariant::signature::Child::Static {
child: &Signature::Str,
},
value: zvariant::signature::Child::Static {
child: &Signature::Variant,
},
};
#[cfg(feature = "query_apply")]
pub(crate) type NmConnectionDbusValue<'a> =
HashMap<&'a str, HashMap<&'a str, zvariant::Value<'a>>>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum NmSettingsConnectionFlag {
Unsaved = 1,
NmGenerated = 2,
Volatile = 4,
External = 8,
}
#[cfg(feature = "query_apply")]
fn from_u32_to_vec_nm_conn_flags(i: u32) -> Vec<NmSettingsConnectionFlag> {
let mut ret = Vec::new();
if i & NmSettingsConnectionFlag::Unsaved as u32 > 0 {
ret.push(NmSettingsConnectionFlag::Unsaved);
}
if i & NmSettingsConnectionFlag::NmGenerated as u32 > 0 {
ret.push(NmSettingsConnectionFlag::NmGenerated);
}
if i & NmSettingsConnectionFlag::Volatile as u32 > 0 {
ret.push(NmSettingsConnectionFlag::Volatile);
}
if i & NmSettingsConnectionFlag::External as u32 > 0 {
ret.push(NmSettingsConnectionFlag::External);
}
ret
}
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
#[serde(try_from = "NmConnectionDbusOwnedValue")]
#[non_exhaustive]
pub struct NmConnection {
pub connection: Option<NmSettingConnection>,
pub bond: Option<NmSettingBond>,
pub bond_port: Option<NmSettingBondPort>,
pub bridge: Option<NmSettingBridge>,
pub bridge_port: Option<NmSettingBridgePort>,
pub ipv4: Option<NmSettingIp>,
pub ipv6: Option<NmSettingIp>,
pub ovs_bridge: Option<NmSettingOvsBridge>,
pub ovs_port: Option<NmSettingOvsPort>,
pub ovs_iface: Option<NmSettingOvsIface>,
pub ovs_ext_ids: Option<NmSettingOvsExtIds>,
pub ovs_other_config: Option<NmSettingOvsOtherConfig>,
pub ovs_patch: Option<NmSettingOvsPatch>,
pub ovs_dpdk: Option<NmSettingOvsDpdk>,
pub wired: Option<NmSettingWired>,
pub vlan: Option<NmSettingVlan>,
pub vxlan: Option<NmSettingVxlan>,
pub mac_vlan: Option<NmSettingMacVlan>,
pub sriov: Option<NmSettingSriov>,
pub vrf: Option<NmSettingVrf>,
pub veth: Option<NmSettingVeth>,
pub ieee8021x: Option<NmSetting8021X>,
pub user: Option<NmSettingUser>,
pub ethtool: Option<NmSettingEthtool>,
pub infiniband: Option<NmSettingInfiniBand>,
pub loopback: Option<NmSettingLoopback>,
pub macsec: Option<NmSettingMacSec>,
pub hsr: Option<NmSettingHsr>,
pub vpn: Option<NmSettingVpn>,
pub ipvlan: Option<NmSettingIpVlan>,
pub ip_tunnel: Option<NmSettingIpTunnel>,
#[serde(rename = "match")]
pub iface_match: Option<NmSettingMatch>,
#[serde(skip)]
pub obj_path: String,
#[serde(skip)]
pub(crate) flags: Vec<NmSettingsConnectionFlag>,
_other: HashMap<String, HashMap<String, zvariant::OwnedValue>>,
}
impl Type for NmConnection {
const SIGNATURE: &'static Signature =
&Signature::static_dict(&Signature::Str, DBUS_ASV_SIGNATURE);
}
impl TryFrom<NmConnectionDbusOwnedValue> for NmConnection {
type Error = NmError;
fn try_from(
mut v: NmConnectionDbusOwnedValue,
) -> Result<Self, Self::Error> {
Ok(Self {
connection: _from_map!(
v,
"connection",
NmSettingConnection::try_from
)?,
ipv4: _from_map!(v, "ipv4", NmSettingIp::try_from)?,
ipv6: _from_map!(v, "ipv6", NmSettingIp::try_from)?,
bond: _from_map!(v, "bond", NmSettingBond::try_from)?,
bond_port: _from_map!(v, "bond-port", NmSettingBondPort::try_from)?,
bridge: _from_map!(v, "bridge", NmSettingBridge::try_from)?,
bridge_port: _from_map!(
v,
"bridge-port",
NmSettingBridgePort::try_from
)?,
ovs_bridge: _from_map!(
v,
"ovs-bridge",
NmSettingOvsBridge::try_from
)?,
ovs_port: _from_map!(v, "ovs-port", NmSettingOvsPort::try_from)?,
ovs_iface: _from_map!(
v,
"ovs-interface",
NmSettingOvsIface::try_from
)?,
ovs_ext_ids: _from_map!(
v,
"ovs-external-ids",
NmSettingOvsExtIds::try_from
)?,
ovs_other_config: _from_map!(
v,
"ovs-other-config",
NmSettingOvsOtherConfig::try_from
)?,
ovs_patch: _from_map!(v, "ovs-patch", NmSettingOvsPatch::try_from)?,
ovs_dpdk: _from_map!(v, "ovs-dpdk", NmSettingOvsDpdk::try_from)?,
wired: _from_map!(v, "802-3-ethernet", NmSettingWired::try_from)?,
vlan: _from_map!(v, "vlan", NmSettingVlan::try_from)?,
vxlan: _from_map!(v, "vxlan", NmSettingVxlan::try_from)?,
sriov: _from_map!(v, "sriov", NmSettingSriov::try_from)?,
mac_vlan: _from_map!(v, "macvlan", NmSettingMacVlan::try_from)?,
macsec: _from_map!(v, "macsec", NmSettingMacSec::try_from)?,
vrf: _from_map!(v, "vrf", NmSettingVrf::try_from)?,
veth: _from_map!(v, "veth", NmSettingVeth::try_from)?,
ieee8021x: _from_map!(v, "802-1x", NmSetting8021X::try_from)?,
user: _from_map!(v, "user", NmSettingUser::try_from)?,
ethtool: _from_map!(v, "ethtool", NmSettingEthtool::try_from)?,
infiniband: _from_map!(
v,
"infiniband",
NmSettingInfiniBand::try_from
)?,
loopback: _from_map!(v, "loopback", NmSettingLoopback::try_from)?,
hsr: _from_map!(v, "hsr", NmSettingHsr::try_from)?,
vpn: _from_map!(v, "vpn", NmSettingVpn::try_from)?,
ip_tunnel: _from_map!(v, "ip-tunnel", NmSettingIpTunnel::try_from)?,
ipvlan: _from_map!(v, "ipvlan", NmSettingIpVlan::try_from)?,
iface_match: _from_map!(v, "match", NmSettingMatch::try_from)?,
_other: v,
..Default::default()
})
}
}
impl NmConnection {
pub fn iface_name(&self) -> Option<&str> {
_connection_inner_string_member!(self, iface_name)
}
pub fn iface_type(&self) -> Option<&NmIfaceType> {
self.connection.as_ref().and_then(|c| c.iface_type.as_ref())
}
pub fn id(&self) -> Option<&str> {
_connection_inner_string_member!(self, id)
}
#[allow(dead_code)]
pub fn controller(&self) -> Option<&str> {
_connection_inner_string_member!(self, controller)
}
pub fn controller_type(&self) -> Option<&NmIfaceType> {
self.connection
.as_ref()
.and_then(|c| c.controller_type.as_ref())
}
#[cfg(feature = "query_apply")]
pub(crate) fn to_value(
&self,
) -> Result<NmConnectionDbusValue<'_>, NmError> {
let mut ret = HashMap::new();
if let Some(con_set) = &self.connection {
ret.insert("connection", con_set.to_value()?);
}
if let Some(bond_set) = &self.bond {
ret.insert("bond", bond_set.to_value()?);
}
if let Some(br_set) = &self.bridge {
ret.insert("bridge", br_set.to_value()?);
}
if let Some(br_port_set) = &self.bridge_port {
ret.insert("bridge-port", br_port_set.to_value()?);
}
if let Some(ipv4_set) = &self.ipv4 {
ret.insert("ipv4", ipv4_set.to_value()?);
}
if let Some(ipv6_set) = &self.ipv6 {
ret.insert("ipv6", ipv6_set.to_value()?);
}
if let Some(ovs_bridge_set) = &self.ovs_bridge {
ret.insert("ovs-bridge", ovs_bridge_set.to_value()?);
}
if let Some(ovs_port_set) = &self.ovs_port {
ret.insert("ovs-port", ovs_port_set.to_value()?);
}
if let Some(ovs_iface_set) = &self.ovs_iface {
ret.insert("ovs-interface", ovs_iface_set.to_value()?);
}
if let Some(ovs_ext_ids) = &self.ovs_ext_ids {
ret.insert("ovs-external-ids", ovs_ext_ids.to_value()?);
}
if let Some(ovs_other_config) = &self.ovs_other_config {
ret.insert("ovs-other-config", ovs_other_config.to_value()?);
}
if let Some(ovs_patch_set) = &self.ovs_patch {
ret.insert("ovs-patch", ovs_patch_set.to_value()?);
}
if let Some(ovs_dpdk_set) = &self.ovs_dpdk {
ret.insert("ovs-dpdk", ovs_dpdk_set.to_value()?);
}
if let Some(wired_set) = &self.wired {
ret.insert("802-3-ethernet", wired_set.to_value()?);
}
if let Some(vlan) = &self.vlan {
ret.insert("vlan", vlan.to_value()?);
}
if let Some(vxlan) = &self.vxlan {
ret.insert("vxlan", vxlan.to_value()?);
}
if let Some(sriov) = &self.sriov {
ret.insert("sriov", sriov.to_value()?);
}
if let Some(mac_vlan) = &self.mac_vlan {
ret.insert("macvlan", mac_vlan.to_value()?);
}
if let Some(macsec) = &self.macsec {
ret.insert("macsec", macsec.to_value()?);
}
if let Some(vrf) = &self.vrf {
ret.insert("vrf", vrf.to_value()?);
}
if let Some(veth) = &self.veth {
ret.insert("veth", veth.to_value()?);
}
if let Some(v) = &self.ieee8021x {
ret.insert("802-1x", v.to_value()?);
}
if let Some(v) = &self.user {
ret.insert("user", v.to_value()?);
}
if let Some(v) = &self.ethtool {
ret.insert("ethtool", v.to_value()?);
}
if let Some(v) = &self.infiniband {
ret.insert("infiniband", v.to_value()?);
}
if let Some(v) = &self.loopback {
ret.insert("loopback", v.to_value()?);
}
if let Some(hsr) = &self.hsr {
ret.insert("hsr", hsr.to_value()?);
}
if let Some(v) = &self.bond_port {
ret.insert("bond-port", v.to_value()?);
}
if let Some(v) = &self.vpn {
ret.insert("vpn", v.to_value()?);
}
if let Some(v) = &self.ip_tunnel {
ret.insert("ip-tunnel", v.to_value()?);
}
if let Some(ipvlan) = &self.ipvlan {
ret.insert("ipvlan", ipvlan.to_value()?);
}
if let Some(iface_match) = &self.iface_match {
ret.insert("match", iface_match.to_value()?);
}
for (key, setting_value) in &self._other {
let mut other_setting_value: HashMap<&str, zvariant::Value> =
HashMap::new();
for (sub_key, sub_value) in setting_value {
other_setting_value.insert(
sub_key.as_str(),
zvariant::Value::from(sub_value.clone()),
);
}
ret.insert(key, other_setting_value);
}
Ok(ret)
}
pub fn uuid(&self) -> Option<&str> {
if let Some(nm_conn_set) = &self.connection
&& let Some(ref uuid) = nm_conn_set.uuid
{
return Some(uuid);
}
None
}
pub fn is_multi_connect(&self) -> bool {
matches!(
self.connection.as_ref().and_then(|c| c.multi_connect),
Some(NmConnectionMultiConnect::Multiple)
| Some(NmConnectionMultiConnect::ManualMultiple)
)
}
}
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
#[serde(try_from = "DbusDictionary")]
#[non_exhaustive]
pub struct NmSettingConnection {
pub id: Option<String>,
pub uuid: Option<String>,
pub iface_type: Option<NmIfaceType>,
pub iface_name: Option<String>,
pub controller: Option<String>,
pub controller_type: Option<NmIfaceType>,
pub autoconnect: Option<bool>,
pub autoconnect_ports: Option<bool>,
pub autoconnect_priority: Option<i32>,
pub lldp: Option<bool>,
pub mptcp_flags: Option<u32>,
pub multi_connect: Option<NmConnectionMultiConnect>,
pub timestamp: Option<u64>,
_other: HashMap<String, zvariant::OwnedValue>,
}
impl TryFrom<DbusDictionary> for NmSettingConnection {
type Error = NmError;
fn try_from(mut v: DbusDictionary) -> Result<Self, Self::Error> {
Ok(Self {
id: _from_map!(v, "id", String::try_from)?,
uuid: _from_map!(v, "uuid", String::try_from)?,
iface_type: _from_map!(v, "type", NmIfaceType::try_from)?,
iface_name: _from_map!(v, "interface-name", String::try_from)?,
controller: _from_map!(v, "master", String::try_from)?,
controller_type: _from_map!(
v,
"slave-type",
NmIfaceType::try_from
)?,
autoconnect: _from_map!(v, "autoconnect", bool::try_from)?
.or(Some(true)),
autoconnect_ports: NmSettingConnection::i32_to_autoconnect_ports(
_from_map!(v, "autoconnect-slaves", i32::try_from)?,
),
autoconnect_priority: _from_map!(
v,
"autoconnect-priority",
i32::try_from
)?,
lldp: _from_map!(v, "lldp", i32::try_from)?.map(|i| i == 1),
mptcp_flags: _from_map!(v, "mptcp-flags", u32::try_from)?,
multi_connect: _from_map!(v, "multi-connect", i32::try_from)?
.map(NmConnectionMultiConnect::from),
timestamp: _from_map!(v, "timestamp", u64::try_from)?,
_other: v,
})
}
}
impl ToDbusValue for NmSettingConnection {
fn to_value(&self) -> Result<HashMap<&str, zvariant::Value<'_>>, NmError> {
let mut ret = HashMap::new();
if let Some(v) = &self.id {
ret.insert("id", zvariant::Value::new(v.as_str()));
}
if let Some(v) = &self.uuid {
ret.insert("uuid", zvariant::Value::new(v.as_str()));
}
if let Some(v) = &self.iface_type {
ret.insert("type", zvariant::Value::new(v.to_string()));
}
if let Some(v) = &self.iface_name {
ret.insert("interface-name", zvariant::Value::new(v.as_str()));
}
if let Some(v) = &self.controller {
ret.insert("master", zvariant::Value::new(v.as_str()));
}
if let Some(v) = &self.controller_type {
ret.insert("slave-type", zvariant::Value::new(v.to_string()));
}
if let Some(v) = &self.lldp {
ret.insert("lldp", zvariant::Value::new(*v as i32));
}
if let Some(v) = &self.mptcp_flags {
ret.insert("mptcp-flags", zvariant::Value::new(v));
}
if let Some(v) = self.multi_connect {
ret.insert("multi-connect", zvariant::Value::new(i32::from(v)));
}
ret.insert(
"autoconnect",
if let Some(false) = &self.autoconnect {
zvariant::Value::new(false)
} else {
zvariant::Value::new(true)
},
);
ret.insert(
"autoconnect-slaves",
match &self.autoconnect_ports {
Some(true) => zvariant::Value::new(NM_AUTOCONENCT_PORT_YES),
Some(false) => zvariant::Value::new(NM_AUTOCONENCT_PORT_NO),
None => zvariant::Value::new(NM_AUTOCONENCT_PORT_DEFAULT),
},
);
if let Some(v) = self.timestamp {
ret.insert("timestamp", zvariant::Value::new(v));
}
ret.extend(self._other.iter().map(|(key, value)| {
(key.as_str(), zvariant::Value::from(value.clone()))
}));
Ok(ret)
}
}
impl NmSettingConnection {
fn i32_to_autoconnect_ports(val: Option<i32>) -> Option<bool> {
match val {
Some(NM_AUTOCONENCT_PORT_YES) => Some(true),
Some(NM_AUTOCONENCT_PORT_NO) => Some(false),
Some(v) => {
warn!("Unknown autoconnect-ports value {v}");
None
}
None => Some(true),
}
}
}
#[cfg(feature = "query_apply")]
pub(crate) async fn nm_con_get_from_obj_path(
dbus_con: &zbus::Connection,
con_obj_path: &str,
) -> Result<NmConnection, NmError> {
let proxy = zbus::Proxy::new(
dbus_con,
super::super::dbus::NM_DBUS_INTERFACE_ROOT,
con_obj_path,
super::super::dbus::NM_DBUS_INTERFACE_SETTING,
)
.await?;
let mut nm_conn = proxy
.call::<&str, (), NmConnection>("GetSettings", &())
.await?;
nm_conn.obj_path = con_obj_path.to_string();
if let Some(ieee_8021x_conf) = nm_conn.ieee8021x.as_mut()
&& let Ok(nm_secrets) = proxy
.call::<&str, &str, NmConnectionDbusOwnedValue>(
"GetSecrets",
&"802-1x",
)
.await
&& let Some(nm_secret) = nm_secrets.get("802-1x")
{
ieee_8021x_conf.fill_secrets(nm_secret);
}
if let Some(macsec_conf) = nm_conn.macsec.as_mut()
&& let Ok(nm_secrets) = proxy
.call::<&str, &str, NmConnectionDbusOwnedValue>(
"GetSecrets",
&"macsec",
)
.await
&& let Some(nm_secret) = nm_secrets.get("macsec")
{
macsec_conf.fill_secrets(nm_secret);
}
if let Some(vpn_conf) = nm_conn.vpn.as_mut()
&& let Ok(nm_secrets) = proxy
.call::<&str, &str, NmConnectionDbusOwnedValue>(
"GetSecrets",
&"vpn",
)
.await
&& let Some(nm_secret) = nm_secrets.get("vpn")
{
vpn_conf.fill_secrets(nm_secret);
}
if let Ok(flags) = proxy.get_property::<u32>("Flags").await {
nm_conn.flags = from_u32_to_vec_nm_conn_flags(flags);
}
Ok(nm_conn)
}
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
#[serde(try_from = "DbusDictionary")]
#[non_exhaustive]
pub struct NmRange {
pub start: u64,
pub end: u64,
_other: DbusDictionary,
}
impl TryFrom<DbusDictionary> for NmRange {
type Error = NmError;
fn try_from(mut v: DbusDictionary) -> Result<Self, Self::Error> {
Ok(Self {
start: _from_map!(v, "start", u64::try_from)?.unwrap_or_default(),
end: _from_map!(v, "end", u64::try_from)?.unwrap_or_default(),
_other: v,
})
}
}
impl NmRange {
pub fn to_value(&self) -> Result<zvariant::Value<'_>, NmError> {
let mut ret = zvariant::Dict::new(&Signature::Str, &Signature::Variant);
ret.append(
zvariant::Value::new("start"),
zvariant::Value::new(zvariant::Value::U64(self.start)),
)?;
ret.append(
zvariant::Value::new("end"),
zvariant::Value::new(zvariant::Value::U64(self.end)),
)?;
Ok(zvariant::Value::Dict(ret))
}
}
const NM_CONNECTION_MULTI_CONNECT_DEFAULT: i32 = 0;
const NM_CONNECTION_MULTI_CONNECT_SINGLE: i32 = 1;
const NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE: i32 = 2;
const NM_CONNECTION_MULTI_CONNECT_MULTIPLE: i32 = 3;
#[derive(Debug, Copy, Clone, PartialEq, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub enum NmConnectionMultiConnect {
#[default]
Default,
Single,
ManualMultiple,
Multiple,
Other(i32),
}
impl From<i32> for NmConnectionMultiConnect {
fn from(d: i32) -> Self {
match d {
NM_CONNECTION_MULTI_CONNECT_DEFAULT => Self::Default,
NM_CONNECTION_MULTI_CONNECT_SINGLE => Self::Single,
NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE => Self::ManualMultiple,
NM_CONNECTION_MULTI_CONNECT_MULTIPLE => Self::Multiple,
_ => Self::Other(d),
}
}
}
impl From<NmConnectionMultiConnect> for i32 {
fn from(v: NmConnectionMultiConnect) -> i32 {
match v {
NmConnectionMultiConnect::Default => {
NM_CONNECTION_MULTI_CONNECT_DEFAULT
}
NmConnectionMultiConnect::Single => {
NM_CONNECTION_MULTI_CONNECT_SINGLE
}
NmConnectionMultiConnect::ManualMultiple => {
NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE
}
NmConnectionMultiConnect::Multiple => {
NM_CONNECTION_MULTI_CONNECT_MULTIPLE
}
NmConnectionMultiConnect::Other(i) => i,
}
}
}