Skip to content

Commit

Permalink
Add wireless migration
Browse files Browse the repository at this point in the history
  • Loading branch information
jcronenberg committed Jan 25, 2024
1 parent b990216 commit fdc5d92
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 0 deletions.
1 change: 1 addition & 0 deletions rust/migrate-wicked/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/migrate-wicked/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] }
serde_ignored = "0.1.9"
uuid = { version = "1.3.4", features = ["v4"] }
async-trait = "0.1.77"
macaddr = "1.0"

[[bin]]
name = "migrate-wicked"
Expand Down
17 changes: 17 additions & 0 deletions rust/migrate-wicked/src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::bond::Bond;
use crate::wireless::Wireless;
use crate::MIGRATION_SETTINGS;
use agama_dbus_server::network::model::{
self, IpConfig, IpRoute, Ipv4Method, Ipv6Method, MacAddress,
Expand Down Expand Up @@ -27,6 +28,8 @@ pub struct Interface {
pub dummy: Option<Dummy>,
pub ethernet: Option<Ethernet>,
pub bond: Option<Bond>,
#[serde(skip_serializing_if = "Option::is_none")]
pub wireless: Option<Wireless>,
#[serde(rename = "@origin")]
pub origin: String,
}
Expand Down Expand Up @@ -152,6 +155,20 @@ impl Interface {
connection.mac_address = MacAddress::try_from(&bond.address)?;
connection.config = bond.into();
connections.push(connection);
} else if let Some(wireless) = &self.wireless {
if let Some(networks) = &wireless.networks {
if networks.len() > 1 {
log::info!("{} has multiple networks defined, these will be split into different connections in NM", connection.id);
}
for (i, network) in networks.iter().enumerate() {
let mut wireless_connection = connection.clone();
if networks.len() > 1 {
wireless_connection.id.push_str(&format!("-{}", i));
}
wireless_connection.config = network.try_into()?;
connections.push(wireless_connection);
}
}
} else {
connections.push(connection);
}
Expand Down
1 change: 1 addition & 0 deletions rust/migrate-wicked/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod bond;
mod interface;
mod migrate;
mod reader;
mod wireless;

use clap::builder::TypedValueParser;
use clap::{Args, Parser, Subcommand};
Expand Down
173 changes: 173 additions & 0 deletions rust/migrate-wicked/src/wireless.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use crate::MIGRATION_SETTINGS;
use agama_dbus_server::network::model::{self, WepAuthAlg, WepKeyType, WepSecurity};
use agama_lib::network::types::SSID;
use anyhow::anyhow;
use macaddr::MacAddr6;
use serde::{Deserialize, Deserializer, Serialize};
use serde_with::formats::CommaSeparator;
use serde_with::StringWithSeparator;
use serde_with::{serde_as, skip_serializing_none, DeserializeFromStr, SerializeDisplay};
use std::str::FromStr;
use strum_macros::{Display, EnumString};

#[skip_serializing_none]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Wireless {
#[serde(rename = "ap-scan")]
pub ap_scan: u32,
#[serde(default)]
#[serde(deserialize_with = "unwrap_wireless_networks")]
pub networks: Option<Vec<Network>>,
}

#[serde_as]
#[skip_serializing_none]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Network {
pub essid: String,
#[serde(rename = "scan-ssid")]
pub scan_ssid: bool,
pub mode: WickedWirelessMode,
#[serde(rename = "wpa-psk")]
pub wpa_psk: Option<WpaPsk>,
#[serde(default)]
#[serde(rename = "key-management")]
#[serde_as(as = "StringWithSeparator::<CommaSeparator, String>")]
pub key_management: Vec<String>,
pub channel: Option<u32>,
#[serde(rename = "access-point")]
pub access_point: Option<String>,
pub wep: Option<Wep>,
}

#[derive(Default, Debug, PartialEq, SerializeDisplay, DeserializeFromStr, EnumString, Display)]
#[strum(serialize_all = "kebab-case")]
pub enum WickedWirelessMode {
AdHoc = 0,
#[default]
Infrastructure = 1,
AP = 2,
}

impl From<&WickedWirelessMode> for model::WirelessMode {
fn from(value: &WickedWirelessMode) -> Self {
match value {
WickedWirelessMode::AdHoc => model::WirelessMode::AdHoc,
WickedWirelessMode::Infrastructure => model::WirelessMode::Infra,
WickedWirelessMode::AP => model::WirelessMode::AP,
}
}
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct WpaPsk {
pub passphrase: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Wep {
#[serde(rename = "auth-algo")]
pub auth_algo: String,
#[serde(rename = "default-key")]
pub default_key: u32,
pub key: Vec<String>,
}

fn unwrap_wireless_networks<'de, D>(deserializer: D) -> Result<Option<Vec<Network>>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
struct Networks {
network: Vec<Network>,
}
Ok(Some(Networks::deserialize(deserializer)?.network))
}

fn wireless_security_protocol(
wicked_value: &[String],
) -> Result<model::SecurityProtocol, anyhow::Error> {
if wicked_value.contains(&"wpa-psk".to_string())
|| wicked_value.contains(&"wpa-psk-sha256".to_string())
{
Ok(model::SecurityProtocol::WPA2)
} else if wicked_value.contains(&"sae".to_string()) {
Ok(model::SecurityProtocol::WPA3Personal)
} else if wicked_value.contains(&"wpa-eap".to_string())
|| wicked_value.contains(&"wpa-eap-sha256".to_string())
{
Ok(model::SecurityProtocol::WPA2Enterprise)
} else if wicked_value.contains(&"owe".to_string()) {
Ok(model::SecurityProtocol::OWE)
} else if wicked_value.contains(&"wpa-eap-suite-b-192".to_string()) {
Ok(model::SecurityProtocol::WPA3Only)
} else if wicked_value.contains(&"none".to_string()) {
Ok(model::SecurityProtocol::WEP)
} else {
Err(anyhow!("Unrecognized key-management protocol"))
}
}

impl TryFrom<&Network> for model::ConnectionConfig {
type Error = anyhow::Error;
fn try_from(network: &Network) -> Result<Self, Self::Error> {
let settings = MIGRATION_SETTINGS.get().unwrap();
let mut config = model::WirelessConfig {
ssid: SSID(network.essid.as_bytes().to_vec()),
..Default::default()
};

if network.key_management.len() > 1 && settings.continue_migration {
log::warn!("Migration of multiple key-management algorithms isn't supported")
} else if network.key_management.len() > 1 {
return Err(anyhow!(
"Migration of multiple key-management algorithms isn't supported"
));
}
config.security = wireless_security_protocol(&network.key_management)?;

if let Some(wpa_psk) = &network.wpa_psk {
config.password = Some(wpa_psk.passphrase.clone())
}
if let Some(channel) = network.channel {
config.channel = Some(channel);
if channel <= 14 {
config.band = Some("bg".try_into().unwrap());
} else {
config.band = Some("a".try_into().unwrap());
}
log::warn!(
"NetworkManager requires setting a band for wireless when a channel is set. The band has been set to \"{}\". This may in certain regions be incorrect.",
config.band.unwrap()
);
}
if let Some(access_point) = &network.access_point {
config.bssid = Some(MacAddr6::from_str(access_point)?);
}

if let Some(wep) = &network.wep {
// filter out `s:`, `h:`, `:`, and `-` of wep keys
let keys: Vec<String> = wep
.key
.clone()
.into_iter()
.map(|mut x| {
x = x.replace("s:", "");
x = x.replace("h:", "");
x = x.replace(':', "");
x.replace('-', "")
})
.collect();
let wep_security = WepSecurity {
auth_alg: WepAuthAlg::try_from(wep.auth_algo.as_str())?,
wep_key_type: WepKeyType::Key,
keys,
wep_key_index: wep.default_key,
};
config.wep_security = Some(wep_security);
}

config.mode = (&network.mode).into();
Ok(model::ConnectionConfig::Wireless(config))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[connection]
id=wlan0-0
uuid=1089cc84-f99f-4bb8-8a0d-fafde6544e85
type=wifi
interface-name=wlan0

[wifi]
band=a
bssid=12:34:56:78:9A:BC
channel=100
mode=adhoc
ssid=example_ssid

[wifi-security]
key-mgmt=wpa-psk
psk=example_passwd

[match]

[ipv4]
method=disabled

[ipv6]
addr-gen-mode=default
method=disabled

[proxy]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[connection]
id=wlan0-1
uuid=b0c56f00-cf58-4c6c-b6dd-edf97a612063
type=wifi
interface-name=wlan0

[wifi]
mode=adhoc
ssid=example_ssid2

[wifi-security]
key-mgmt=wpa-psk
psk=example_passwd2

[match]

[ipv4]
method=disabled

[ipv6]
addr-gen-mode=default
method=disabled

[proxy]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[connection]
id=wlan1
uuid=3bc52d5e-4095-4e1a-9976-d46f766bb627
type=wifi
interface-name=wlan1

[wifi]
bssid=12:34:56:78:9A:BC
mode=infrastructure
ssid=test

[wifi-security]
auth-alg=shared
key-mgmt=none
wep-key-type=1
wep-key0=hello
wep-key1=5b73215e232f4c577c5073455d
wep-tx-keyidx=1

[match]

[ipv4]
method=disabled

[ipv6]
addr-gen-mode=default
method=disabled

[proxy]
77 changes: 77 additions & 0 deletions rust/migrate-wicked/tests/wireless/wicked_xml/wireless.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<interface origin="compat:suse:/etc/sysconfig/network/ifcfg-wlan0">
<name>wlan0</name>
<control>
<mode>manual</mode>
</control>
<firewall/>
<wireless>
<ap-scan>1</ap-scan>
<networks>
<network>
<essid>example_ssid</essid>
<channel>100</channel>
<access-point>12:34:56:78:9A:BC</access-point>
<scan-ssid>true</scan-ssid>
<mode>ad-hoc</mode>
<key-management>wpa-psk,wpa-psk-sha256,sae</key-management>
<wpa-psk>
<passphrase>example_passwd</passphrase>
<pairwise-cipher>CCMP</pairwise-cipher>
<group-cipher>CCMP</group-cipher>
<pmf>optional</pmf>
</wpa-psk>
</network>
<network>
<essid>example_ssid2</essid>
<scan-ssid>true</scan-ssid>
<mode>ad-hoc</mode>
<key-management>wpa-psk,wpa-psk-sha256,sae</key-management>
<wpa-psk>
<passphrase>example_passwd2</passphrase>
<pairwise-cipher>CCMP</pairwise-cipher>
<group-cipher>CCMP</group-cipher>
<pmf>optional</pmf>
</wpa-psk>
</network>
</networks>
</wireless>
<link/>
<ipv4>
<enabled>false</enabled>
</ipv4>
<ipv6>
<enabled>false</enabled>
</ipv6>
</interface>
<interface origin="compat:suse:/etc/sysconfig/network/ifcfg-wlan1">
<name>wlan1</name>
<control>
<mode>manual</mode>
</control>
<firewall/>
<wireless>
<ap-scan>1</ap-scan>
<networks>
<network>
<essid>test</essid>
<scan-ssid>true</scan-ssid>
<mode>infrastructure</mode>
<access-point>12:34:56:78:9a:bc</access-point>
<key-management>none</key-management>
<wep>
<auth-algo>shared</auth-algo>
<default-key>1</default-key>
<key>s:hello</key>
<key>5b73215e232f4c577c5073455d</key>
</wep>
</network>
</networks>
</wireless>
<link/>
<ipv4>
<enabled>false</enabled>
</ipv4>
<ipv6>
<enabled>false</enabled>
</ipv6>
</interface>

0 comments on commit fdc5d92

Please sign in to comment.