diff --git a/rust/agama-dbus-server/src/network/error.rs b/rust/agama-dbus-server/src/network/error.rs index 6b674b7efb..04819a971b 100644 --- a/rust/agama-dbus-server/src/network/error.rs +++ b/rust/agama-dbus-server/src/network/error.rs @@ -29,6 +29,10 @@ pub enum NetworkStateError { NotControllerConnection(String), #[error("Unexpected configuration")] UnexpectedConfiguration, + #[error("Invalid WEP authentication algorithm: '{0}'")] + InvalidWEPAuthAlg(String), + #[error("Invalid WEP key type: '{0}'")] + InvalidWEPKeyType(u32), } impl From for zbus::fdo::Error { diff --git a/rust/agama-dbus-server/src/network/model.rs b/rust/agama-dbus-server/src/network/model.rs index 0d59fa171c..67fbbb2484 100644 --- a/rust/agama-dbus-server/src/network/model.rs +++ b/rust/agama-dbus-server/src/network/model.rs @@ -740,6 +740,10 @@ pub struct WirelessConfig { pub ssid: SSID, pub password: Option, pub security: SecurityProtocol, + pub band: Option, + pub channel: Option, + pub bssid: Option, + pub wep_security: Option, } impl TryFrom for WirelessConfig { @@ -837,6 +841,98 @@ impl TryFrom<&str> for SecurityProtocol { } } +#[derive(Debug, Default, PartialEq, Clone)] +pub struct WEPSecurity { + pub auth_alg: WEPAuthAlg, + pub wep_key_type: WEPKeyType, + pub keys: Vec, + pub wep_key_index: u32, +} + +#[derive(Debug, Default, PartialEq, Clone)] +pub enum WEPKeyType { + #[default] + Unknown = 0, + Key = 1, + Passphrase = 2, +} + +impl TryFrom for WEPKeyType { + type Error = NetworkStateError; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(WEPKeyType::Unknown), + 1 => Ok(WEPKeyType::Key), + 2 => Ok(WEPKeyType::Passphrase), + _ => Err(NetworkStateError::InvalidWEPKeyType(value)), + } + } +} + +#[derive(Debug, Default, PartialEq, Clone)] +pub enum WEPAuthAlg { + #[default] + Unset, + Open, + Shared, + Leap, +} + +impl TryFrom<&str> for WEPAuthAlg { + type Error = NetworkStateError; + + fn try_from(value: &str) -> Result { + match value { + "open" => Ok(WEPAuthAlg::Open), + "shared" => Ok(WEPAuthAlg::Shared), + "leap" => Ok(WEPAuthAlg::Leap), + "" => Ok(WEPAuthAlg::Unset), + _ => Err(NetworkStateError::InvalidWEPAuthAlg(value.to_string())), + } + } +} + +impl fmt::Display for WEPAuthAlg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = match &self { + WEPAuthAlg::Open => "open", + WEPAuthAlg::Shared => "shared", + WEPAuthAlg::Leap => "shared", + WEPAuthAlg::Unset => "", + }; + write!(f, "{}", name) + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum WirelessBand { + A, // 5GHz + BG, // 2.4GHz +} + +impl fmt::Display for WirelessBand { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let value = match &self { + WirelessBand::A => "a", + WirelessBand::BG => "bg", + }; + write!(f, "{}", value) + } +} + +impl TryFrom<&str> for WirelessBand { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + match value { + "a" => Ok(WirelessBand::A), + "bg" => Ok(WirelessBand::BG), + _ => Err(anyhow::anyhow!("Invalid band: {}", value)), + } + } +} + #[derive(Debug, Default, Clone, PartialEq)] pub struct BondOptions(pub HashMap); diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index b28d654dd3..9d0655f54d 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -9,6 +9,7 @@ use agama_lib::{ network::types::{BondMode, SSID}, }; use cidr::IpInet; +use macaddr::MacAddr6; use std::{collections::HashMap, net::IpAddr, str::FromStr}; use uuid::Uuid; use zbus::zvariant::{self, OwnedValue, Value}; @@ -321,18 +322,53 @@ fn wireless_config_to_dbus<'a>( config: &'a WirelessConfig, mac_address: &MacAddress, ) -> NestedHash<'a> { - let wireless: HashMap<&str, zvariant::Value> = HashMap::from([ + let mut wireless: HashMap<&str, zvariant::Value> = HashMap::from([ ("mode", Value::new(config.mode.to_string())), ("ssid", Value::new(config.ssid.to_vec())), ("assigned-mac-address", Value::new(mac_address.to_string())), ]); + if let Some(band) = &config.band { + wireless.insert("band", band.to_string().into()); + if let Some(channel) = config.channel { + wireless.insert("channel", channel.into()); + } + } + if let Some(bssid) = &config.bssid { + wireless.insert("bssid", bssid.as_bytes().into()); + } + let mut security: HashMap<&str, zvariant::Value> = HashMap::from([("key-mgmt", config.security.to_string().into())]); if let Some(password) = &config.password { security.insert("psk", password.to_string().into()); } + if let Some(wep_security) = &config.wep_security { + security.insert( + "wep-key-type", + (wep_security.wep_key_type.clone() as u32).into(), + ); + security.insert("auth-alg", wep_security.auth_alg.to_string().into()); + for (i, wep_key) in wep_security.keys.clone().into_iter().enumerate() { + security.insert( + // FIXME: lifetimes are fun + if i == 0 { + "wep-key0" + } else if i == 1 { + "wep-key1" + } else if i == 2 { + "wep-key2" + } else if i == 3 { + "wep-key3" + } else { + break; + }, + wep_key.into(), + ); + } + security.insert("wep-tx-keyidx", wep_security.wep_key_index.into()); + } NestedHash::from([(WIRELESS_KEY, wireless), (WIRELESS_SECURITY_KEY, security)]) } @@ -668,9 +704,51 @@ fn wireless_config_from_dbus(conn: &OwnedNestedHash) -> Option { ..Default::default() }; + if let Some(band) = wireless.get("band") { + wireless_config.band = Some(band.downcast_ref::()?.try_into().ok()?) + } + if let Some(channel) = wireless.get("channel") { + wireless_config.channel = Some(*channel.downcast_ref()?); + } + if let Some(bssid) = wireless.get("bssid") { + let bssid: &zvariant::Array = bssid.downcast_ref()?; + let bssid: Vec = bssid + .get() + .iter() + .map(|u| *u.downcast_ref::().unwrap()) + .collect(); + wireless_config.bssid = Some(MacAddr6::new( + *bssid.first()?, + *bssid.get(1)?, + *bssid.get(2)?, + *bssid.get(3)?, + *bssid.get(4)?, + *bssid.get(5)?, + )); + } + if let Some(security) = conn.get(WIRELESS_SECURITY_KEY) { let key_mgmt: &str = security.get("key-mgmt")?.downcast_ref()?; wireless_config.security = NmKeyManagement(key_mgmt.to_string()).try_into().ok()?; + + let wep_key_type = security + .get("wep-key-type") + .and_then(|alg| WEPKeyType::try_from(*alg.downcast_ref::()?).ok()) + .unwrap_or_default(); + let auth_alg = security + .get("auth-alg") + .and_then(|alg| WEPAuthAlg::try_from(alg.downcast_ref()?).ok()) + .unwrap_or_default(); + let wep_key_index = security + .get("wep-tx-keyidx") + .and_then(|idx| idx.downcast_ref::().cloned()) + .unwrap_or_default(); + wireless_config.wep_security = Some(WEPSecurity { + wep_key_type, + auth_alg, + wep_key_index, + ..Default::default() + }); } Some(wireless_config) @@ -917,10 +995,23 @@ mod test { "assigned-mac-address".to_string(), Value::new("13:45:67:89:AB:CD").to_owned(), ), + ("band".to_string(), Value::new("a").to_owned()), + ("channel".to_string(), Value::new(32_u32).to_owned()), + ( + "bssid".to_string(), + Value::new(vec![18_u8, 52_u8, 86_u8, 120_u8, 154_u8, 188_u8]).to_owned(), + ), ]); - let security_section = - HashMap::from([("key-mgmt".to_string(), Value::new("wpa-psk").to_owned())]); + let security_section = HashMap::from([ + ("key-mgmt".to_string(), Value::new("wpa-psk").to_owned()), + ( + "wep-key-type".to_string(), + Value::new(WEPKeyType::Key as u32).to_owned(), + ), + ("auth-alg".to_string(), Value::new("open").to_owned()), + ("wep-tx-keyidx".to_string(), Value::new(1_u32).to_owned()), + ]); let dbus_conn = HashMap::from([ ("connection".to_string(), connection_section), @@ -934,7 +1025,17 @@ mod test { if let ConnectionConfig::Wireless(wireless) = &connection.config { assert_eq!(wireless.ssid, SSID(vec![97, 103, 97, 109, 97])); assert_eq!(wireless.mode, WirelessMode::Infra); - assert_eq!(wireless.security, SecurityProtocol::WPA2) + assert_eq!(wireless.security, SecurityProtocol::WPA2); + assert_eq!(wireless.band, Some(WirelessBand::A)); + assert_eq!(wireless.channel, Some(32_u32)); + assert_eq!( + wireless.bssid, + Some(macaddr::MacAddr6::from_str("12:34:56:78:9A:BC").unwrap()) + ); + let wep_security = wireless.wep_security.as_ref().unwrap(); + assert_eq!(wep_security.wep_key_type, WEPKeyType::Key); + assert_eq!(wep_security.auth_alg, WEPAuthAlg::Open); + assert_eq!(wep_security.wep_key_index, 1); } } @@ -967,7 +1068,20 @@ mod test { let config = WirelessConfig { mode: WirelessMode::Infra, security: SecurityProtocol::WPA2, + password: Some("wpa-password".to_string()), ssid: SSID(vec![97, 103, 97, 109, 97]), + band: Some(WirelessBand::BG), + channel: Some(10), + bssid: Some(macaddr::MacAddr6::from_str("12:34:56:78:9A:BC").unwrap()), + wep_security: Some(WEPSecurity { + auth_alg: WEPAuthAlg::Open, + wep_key_type: WEPKeyType::Key, + wep_key_index: 1, + keys: vec![ + "5b73215e232f4c577c5073455d".to_string(), + "hello".to_string(), + ], + }), ..Default::default() }; let mut wireless = build_base_connection(); @@ -992,9 +1106,54 @@ mod test { .collect(); assert_eq!(ssid, "agama".as_bytes()); + let band: &str = wireless.get("band").unwrap().downcast_ref().unwrap(); + assert_eq!(band, "bg"); + + let channel: u32 = *wireless.get("channel").unwrap().downcast_ref().unwrap(); + assert_eq!(channel, 10); + + let bssid: &zvariant::Array = wireless.get("bssid").unwrap().downcast_ref().unwrap(); + let bssid: Vec = bssid + .get() + .iter() + .map(|u| *u.downcast_ref::().unwrap()) + .collect(); + assert_eq!(bssid, vec![18, 52, 86, 120, 154, 188]); + let security = wireless_dbus.get(WIRELESS_SECURITY_KEY).unwrap(); let key_mgmt: &str = security.get("key-mgmt").unwrap().downcast_ref().unwrap(); assert_eq!(key_mgmt, "wpa-psk"); + + let password: &str = security.get("psk").unwrap().downcast_ref().unwrap(); + assert_eq!(password, "wpa-password"); + + let auth_alg: WEPAuthAlg = security + .get("auth-alg") + .unwrap() + .downcast_ref::() + .unwrap() + .try_into() + .unwrap(); + assert_eq!(auth_alg, WEPAuthAlg::Open); + + let wep_key_type: u32 = *security + .get("wep-key-type") + .unwrap() + .downcast_ref::() + .unwrap(); + assert_eq!(wep_key_type, WEPKeyType::Key as u32); + + let wep_key_index: u32 = *security + .get("wep-tx-keyidx") + .unwrap() + .downcast_ref() + .unwrap(); + assert_eq!(wep_key_index, 1); + + let wep_key0: &str = security.get("wep-key0").unwrap().downcast_ref().unwrap(); + assert_eq!(wep_key0, "5b73215e232f4c577c5073455d"); + let wep_key1: &str = security.get("wep-key1").unwrap().downcast_ref().unwrap(); + assert_eq!(wep_key1, "hello"); } #[test] diff --git a/rust/package/agama-cli.changes b/rust/package/agama-cli.changes index ccf0d6d60f..1a2521ca72 100644 --- a/rust/package/agama-cli.changes +++ b/rust/package/agama-cli.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Mon Jan 29 10:22:49 UTC 2024 - Jorik Cronenberg + +- Add more wireless options to network model + (gh#openSUSE/agama#1014). + ------------------------------------------------------------------- Thu Jan 23 18:00:00 UTC 2024 - Clemens Famulla-Conrad