Skip to content

Commit

Permalink
Add vlan model (#918)
Browse files Browse the repository at this point in the history
## Problem

Agama does not support VLAN configuration.

## Solution

Add support for VLAN configuration


## Testing

- *Tested manually*
  • Loading branch information
imobachgs authored Jan 23, 2024
2 parents 3cceaa4 + b317e38 commit 6a99bea
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
43 changes: 43 additions & 0 deletions rust/agama-dbus-server/src/network/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ impl Connection {
DeviceType::Loopback => ConnectionConfig::Loopback,
DeviceType::Dummy => ConnectionConfig::Dummy,
DeviceType::Bond => ConnectionConfig::Bond(Default::default()),
DeviceType::Vlan => ConnectionConfig::Vlan(Default::default()),
};
Self {
id,
Expand Down Expand Up @@ -434,6 +435,7 @@ impl Connection {
|| matches!(self.config, ConnectionConfig::Ethernet)
|| matches!(self.config, ConnectionConfig::Dummy)
|| matches!(self.config, ConnectionConfig::Bond(_))
|| matches!(self.config, ConnectionConfig::Vlan(_))
}
}

Expand Down Expand Up @@ -461,6 +463,7 @@ pub enum ConnectionConfig {
Loopback,
Dummy,
Bond(BondConfig),
Vlan(VlanConfig),
}

impl From<BondConfig> for ConnectionConfig {
Expand Down Expand Up @@ -679,6 +682,46 @@ impl From<&IpRoute> for HashMap<&str, Value<'_>> {
}
}

#[derive(Debug, Default, PartialEq, Clone)]
pub enum VlanProtocol {
#[default]
IEEE802_1Q,
IEEE802_1ad,
}

#[derive(Debug, Error)]
#[error("Invalid VlanProtocol: {0}")]
pub struct InvalidVlanProtocol(String);

impl std::str::FromStr for VlanProtocol {
type Err = InvalidVlanProtocol;

fn from_str(s: &str) -> Result<VlanProtocol, Self::Err> {
match s {
"802.1Q" => Ok(VlanProtocol::IEEE802_1Q),
"802.1ad" => Ok(VlanProtocol::IEEE802_1ad),
_ => Err(InvalidVlanProtocol(s.to_string())),
}
}
}

impl fmt::Display for VlanProtocol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match &self {
VlanProtocol::IEEE802_1Q => "802.1Q",
VlanProtocol::IEEE802_1ad => "802.1ad",
};
write!(f, "{}", name)
}
}

#[derive(Debug, Default, PartialEq, Clone)]
pub struct VlanConfig {
pub parent: String,
pub id: u32,
pub protocol: VlanProtocol,
}

#[derive(Debug, Default, PartialEq, Clone)]
pub struct WirelessConfig {
pub mode: WirelessMode,
Expand Down
50 changes: 50 additions & 0 deletions rust/agama-dbus-server/src/network/nm/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const WIRELESS_KEY: &str = "802-11-wireless";
const WIRELESS_SECURITY_KEY: &str = "802-11-wireless-security";
const LOOPBACK_KEY: &str = "loopback";
const DUMMY_KEY: &str = "dummy";
const VLAN_KEY: &str = "vlan";

/// Converts a connection struct into a HashMap that can be sent over D-Bus.
///
Expand Down Expand Up @@ -77,6 +78,10 @@ pub fn connection_to_dbus<'a>(
ConnectionConfig::Dummy => {
connection_dbus.insert("type", DUMMY_KEY.into());
}
ConnectionConfig::Vlan(vlan) => {
connection_dbus.insert("type", VLAN_KEY.into());
result.extend(vlan_config_to_dbus(vlan));
}
_ => {}
}

Expand All @@ -100,6 +105,11 @@ pub fn connection_from_dbus(conn: OwnedNestedHash) -> Option<Connection> {
return Some(connection);
}

if let Some(vlan_config) = vlan_config_from_dbus(&conn) {
connection.config = ConnectionConfig::Vlan(vlan_config);
return Some(connection);
}

if conn.get(DUMMY_KEY).is_some() {
connection.config = ConnectionConfig::Dummy;
return Some(connection);
Expand Down Expand Up @@ -567,6 +577,46 @@ fn bond_config_from_dbus(conn: &OwnedNestedHash) -> Option<BondConfig> {
Some(bond)
}

fn vlan_config_to_dbus(cfg: &VlanConfig) -> NestedHash {
let vlan: HashMap<&str, zvariant::Value> = HashMap::from([
("id", cfg.id.into()),
("parent", cfg.parent.clone().into()),
("protocol", cfg.protocol.to_string().into()),
]);

NestedHash::from([("vlan", vlan)])
}

fn vlan_config_from_dbus(conn: &OwnedNestedHash) -> Option<VlanConfig> {
let Some(vlan) = conn.get(VLAN_KEY) else {
return None;
};

let Some(id) = vlan.get("id") else {
return None;
};
let id = id.downcast_ref::<u32>()?;

let Some(parent) = vlan.get("parent") else {
return None;
};
let parent: &str = parent.downcast_ref()?;

let protocol = match vlan.get("protocol") {
Some(x) => {
let x: &str = x.downcast_ref()?;
VlanProtocol::from_str(x).unwrap_or_default()
}
_ => Default::default(),
};

Some(VlanConfig {
id: *id,
parent: String::from(parent),
protocol,
})
}

/// Determines whether a value is empty.
///
/// TODO: Generalize for other kind of values, like dicts or arrays.
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-lib/src/network/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub enum DeviceType {
Wireless = 2,
Dummy = 3,
Bond = 4,
Vlan = 5,
}

/// Bond mode
Expand Down Expand Up @@ -139,6 +140,7 @@ impl TryFrom<u8> for DeviceType {
2 => Ok(DeviceType::Wireless),
3 => Ok(DeviceType::Dummy),
4 => Ok(DeviceType::Bond),
5 => Ok(DeviceType::Vlan),
_ => Err(InvalidDeviceType(value)),
}
}
Expand Down

0 comments on commit 6a99bea

Please sign in to comment.