use serde::{Deserialize, Serialize};
use crate::{
BaseInterface, ErrorKind, Interface, InterfaceType, Interfaces,
MergedInterfaces, NetworkStateMode, NmstateError, SrIovConfig,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EthernetInterface {
#[serde(flatten)]
pub base: BaseInterface,
#[serde(skip_serializing_if = "Option::is_none")]
pub ethernet: Option<EthernetConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub veth: Option<VethConfig>,
}
impl Default for EthernetInterface {
fn default() -> Self {
let mut base = BaseInterface::new();
base.iface_type = InterfaceType::Ethernet;
Self {
base,
ethernet: None,
veth: None,
}
}
}
impl EthernetInterface {
pub(crate) fn sanitize(&mut self) -> Result<(), NmstateError> {
self.base.iface_type = InterfaceType::Ethernet;
if let Some(sriov_conf) =
self.ethernet.as_mut().and_then(|e| e.sr_iov.as_mut())
{
sriov_conf.sanitize()?
}
Ok(())
}
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum EthernetDuplex {
Full,
Half,
}
impl std::fmt::Display for EthernetDuplex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Full => "full",
Self::Half => "half",
}
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[non_exhaustive]
pub struct EthernetConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub sr_iov: Option<SrIovConfig>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "auto-negotiation",
default,
deserialize_with = "crate::deserializer::option_bool_or_string"
)]
pub auto_neg: Option<bool>,
#[serde(
skip_serializing_if = "Option::is_none",
default,
deserialize_with = "crate::deserializer::option_u32_or_string"
)]
pub speed: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duplex: Option<EthernetDuplex>,
}
impl EthernetConfig {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub struct VethConfig {
pub peer: String,
}
impl MergedInterfaces {
pub(crate) fn process_veth_peer_changes(
&mut self,
) -> Result<(), NmstateError> {
let mut veth_peers: Vec<&str> = Vec::new();
for iface in self.iter().filter(|i| {
i.merged.iface_type() == InterfaceType::Ethernet && i.merged.is_up()
}) {
if let Interface::Ethernet(eth_iface) = &iface.merged
&& let Some(v) =
eth_iface.veth.as_ref().map(|v| v.peer.as_str())
{
veth_peers.push(v);
}
}
for iface in self.iter().filter(|i| {
i.merged.iface_type() == InterfaceType::Ethernet
&& i.is_desired()
&& i.current.is_none()
&& i.merged.is_up()
}) {
if let Some(Interface::Ethernet(eth_iface)) = &iface.desired
&& eth_iface.veth.is_none()
&& self.mode != NetworkStateMode::GenerateConfig
&& !veth_peers.contains(ð_iface.base.name.as_str())
&& !eth_iface.base.name.starts_with("sriov:")
{
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Ethernet interface {} does not exists",
eth_iface.base.name.as_str()
),
));
}
}
let mut pending_deletions: Vec<String> = Vec::new();
for iface in self.iter().filter(|i| {
i.merged.iface_type() == InterfaceType::Ethernet
&& i.is_desired()
&& i.merged.is_up()
&& i.current.is_some()
}) {
if let (
Some(Interface::Ethernet(des_eth_iface)),
Some(Interface::Ethernet(cur_eth_iface)),
) = (iface.desired.as_ref(), iface.current.as_ref())
&& let (Some(veth_conf), Some(cur_veth_conf)) =
(des_eth_iface.veth.as_ref(), cur_eth_iface.veth.as_ref())
&& veth_conf.peer != cur_veth_conf.peer
{
pending_deletions.push(cur_veth_conf.peer.to_string());
}
}
for iface in self.iter().filter(|i| {
i.merged.iface_type() == InterfaceType::Ethernet
&& i.is_desired()
&& i.merged.is_absent()
&& i.current.is_some()
}) {
if let Some(Interface::Ethernet(cur_eth_iface)) =
iface.current.as_ref()
&& let Some(veth_conf) = cur_eth_iface.veth.as_ref()
{
pending_deletions.push(veth_conf.peer.to_string());
}
}
for del_peer in pending_deletions {
if let Some(iface) = self.kernel_ifaces.get_mut(&del_peer) {
iface.mark_as_absent();
}
}
Ok(())
}
}
impl Interfaces {
pub(crate) fn validate_change_veth_ignored_peer(
&self,
current: &Self,
ignored_ifaces: &[(String, InterfaceType)],
) -> Result<(), NmstateError> {
let ignored_veth_ifaces: Vec<&String> = ignored_ifaces
.iter()
.filter_map(|(n, t)| {
if t == &InterfaceType::Ethernet {
Some(n)
} else {
None
}
})
.collect();
for iface in self.kernel_ifaces.values().filter(|i| {
if let Interface::Ethernet(i) = i {
i.veth.is_some()
} else {
false
}
}) {
if let (
Interface::Ethernet(des_iface),
Some(Interface::Ethernet(cur_iface)),
) = (iface, current.get_iface(iface.name(), InterfaceType::Veth))
&& let (Some(des_peer), cur_peer) = (
des_iface.veth.as_ref().map(|v| v.peer.as_str()),
cur_iface.veth.as_ref().map(|v| v.peer.as_str()),
)
{
let cur_peer = if let Some(c) = cur_peer {
c
} else {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Veth interface {} is currently holding peer \
assigned to other namespace Please remove this \
veth pair before changing veth peer to {des_peer}",
iface.name(),
),
);
log::error!("{e}");
return Err(e);
};
if des_peer != cur_peer
&& ignored_veth_ifaces.contains(&&cur_peer.to_string())
{
let e = NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Veth interface {} is currently holding peer {} \
which is marked as ignored. Hence not allowing \
changing its peer to {}. Please remove this veth \
pair before changing veth peer",
iface.name(),
cur_peer,
des_peer
),
);
log::error!("{e}");
return Err(e);
}
}
}
Ok(())
}
pub(crate) fn validate_new_veth_without_peer(
&self,
current: &Self,
) -> Result<(), NmstateError> {
for iface in self.kernel_ifaces.values().filter(|i| {
i.is_up()
&& i.iface_type() == InterfaceType::Veth
&& !current.kernel_ifaces.contains_key(i.name())
}) {
if let Interface::Ethernet(eth_iface) = iface
&& eth_iface.veth.is_none()
{
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Veth interface {} does not exist, peer name is \
required for creating it",
iface.name()
),
));
}
}
Ok(())
}
}