use serde::{Deserialize, Serialize};
use crate::{
BaseInterface, ErrorKind, InterfaceType, NetworkState, NmstateError,
};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct MacSecInterface {
#[serde(flatten)]
pub base: BaseInterface,
#[serde(skip_serializing_if = "Option::is_none")]
pub macsec: Option<MacSecConfig>,
}
impl Default for MacSecInterface {
fn default() -> Self {
Self {
base: BaseInterface {
iface_type: InterfaceType::MacSec,
..Default::default()
},
macsec: None,
}
}
}
impl MacSecInterface {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn sanitize(
&self,
is_desired: bool,
) -> Result<(), NmstateError> {
if is_desired {
if let Some(conf) = &self.macsec {
if conf.mka_cak.is_none() ^ conf.mka_ckn.is_none() {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
"The mka_cak and mka_cnk must be all missing or present.".to_string()
);
log::error!("{}", e);
return Err(e);
}
if let Some(mka_cak) = &conf.mka_cak {
if mka_cak.len() != 32 {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
"The mka_cak must be a string of 32 characters"
.to_string(),
);
log::error!("{}", e);
return Err(e);
}
}
if let Some(mka_ckn) = &conf.mka_ckn {
if mka_ckn.len() > 64
|| mka_ckn.len() < 2
|| mka_ckn.len() % 2 == 1
{
let e = NmstateError::new(
ErrorKind::InvalidArgument,
"The mka_ckn must be a string of even size between 2 and 64 characters".to_string()
);
log::error!("{}", e);
return Err(e);
}
}
}
}
Ok(())
}
pub(crate) fn parent(&self) -> Option<&str> {
self.macsec.as_ref().map(|cfg| cfg.base_iface.as_str())
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
#[derive(Default)]
pub struct MacSecConfig {
pub encrypt: bool,
pub base_iface: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mka_cak: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mka_ckn: Option<String>,
pub port: u32,
pub validation: MacSecValidate,
pub send_sci: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub offload: Option<MacSecOffload>,
}
impl MacSecConfig {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn hide_secrets(&mut self) {
if self.mka_cak.is_some() {
self.mka_cak =
Some(NetworkState::PASSWORD_HID_BY_NMSTATE.to_string());
}
}
}
impl std::fmt::Debug for MacSecConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MacSecConfig")
.field("encrypt", &self.encrypt)
.field("base_iface", &self.base_iface)
.field(
"mka_cak",
&Some(NetworkState::PASSWORD_HID_BY_NMSTATE.to_string()),
)
.field("mka_ckn", &self.mka_ckn)
.field("port", &self.port)
.field("validation", &self.validation)
.field("send_sci", &self.send_sci)
.field("offload", &self.offload)
.finish()
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum MacSecValidate {
Disabled,
Check,
Strict,
}
impl Default for MacSecValidate {
fn default() -> Self {
Self::Disabled
}
}
impl From<MacSecValidate> for u32 {
fn from(v: MacSecValidate) -> u32 {
match v {
MacSecValidate::Disabled => 0,
MacSecValidate::Check => 1,
MacSecValidate::Strict => 2,
}
}
}
impl From<MacSecValidate> for i32 {
fn from(v: MacSecValidate) -> i32 {
match v {
MacSecValidate::Disabled => 0,
MacSecValidate::Check => 1,
MacSecValidate::Strict => 2,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum MacSecOffload {
#[default]
Off,
Phy,
Mac,
}