mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-11 10:58:21 +00:00
235 lines
7.7 KiB
Rust
235 lines
7.7 KiB
Rust
use super::ApplyError;
|
|
use crate::{
|
|
definitions::{config::Config, network::NetworkInterfaceType},
|
|
templates,
|
|
};
|
|
use std::process::Command;
|
|
use std::{error::Error, io::Write};
|
|
use tera::Context;
|
|
use tracing::{error, info};
|
|
|
|
const NETWORKD_CONFIG_PATH: &str = "/etc/systemd/network";
|
|
|
|
pub struct File {
|
|
pub name: String,
|
|
pub content: String,
|
|
}
|
|
|
|
pub fn delete_files_in_folder(path: &str) -> std::io::Result<()> {
|
|
for entry in std::fs::read_dir(path)? {
|
|
let entry = entry?;
|
|
std::fs::remove_file(entry.path())?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn create_files_in_folder(path: &str, files: Vec<File>) -> std::io::Result<()> {
|
|
for file in files {
|
|
let mut f = std::fs::File::create(path.to_string() + "/" + &file.name)?;
|
|
f.write_all(file.content.as_bytes())?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn apply_networkd(pending_config: Config, current_config: Config) -> Result<(), ApplyError> {
|
|
let files = generate_networkd_config_files(pending_config, current_config)?;
|
|
|
|
info!("Deleting old Networkd Configs");
|
|
match delete_files_in_folder(NETWORKD_CONFIG_PATH) {
|
|
Ok(_) => (),
|
|
Err(err) => return Err(ApplyError::IOError(err)),
|
|
}
|
|
|
|
info!("Writing new Networkd Configs");
|
|
match create_files_in_folder(NETWORKD_CONFIG_PATH, files) {
|
|
Ok(_) => (),
|
|
Err(err) => return Err(ApplyError::IOError(err)),
|
|
}
|
|
|
|
info!("Restarting Networkd");
|
|
match Command::new("systemctl")
|
|
.arg("restart")
|
|
.arg("systemd-networkd")
|
|
.output()
|
|
{
|
|
Ok(out) => {
|
|
if out.status.success() {
|
|
Ok(())
|
|
} else {
|
|
Err(ApplyError::ServiceRestartFailed)
|
|
}
|
|
}
|
|
Err(err) => Err(ApplyError::IOError(err)),
|
|
}
|
|
}
|
|
|
|
pub fn generate_networkd_config_files(
|
|
pending_config: Config,
|
|
_current_config: Config,
|
|
) -> Result<Vec<File>, ApplyError> {
|
|
let mut files = Vec::new();
|
|
|
|
// Step 1 Generate vlan netdev files
|
|
for interface in &pending_config.network.interfaces {
|
|
if let NetworkInterfaceType::Vlan { id, .. } = &interface.interface_type {
|
|
let mut context = Context::new();
|
|
context.insert("name", &interface.name);
|
|
context.insert("vlan_id", &id);
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/create-vlan.netdev",
|
|
format!("10-create-vlan-{}.netdev", &interface.name),
|
|
)?);
|
|
}
|
|
}
|
|
|
|
// Step 2 Generate bond netdev files
|
|
for interface in &pending_config.network.interfaces {
|
|
if let NetworkInterfaceType::Bond { .. } = &interface.interface_type {
|
|
let mut context = Context::new();
|
|
context.insert("name", &interface.name);
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/create-bond.netdev",
|
|
format!("20-create-bond-{}.netdev", &interface.name),
|
|
)?);
|
|
|
|
// Create Membership files
|
|
for member in interface
|
|
.interface_type
|
|
.bond_members(pending_config.clone())
|
|
{
|
|
let mut context = Context::new();
|
|
context.insert("bond_name", &interface.name);
|
|
|
|
// if interface is a hardware interface then we want to use device instead
|
|
match member.interface_type {
|
|
NetworkInterfaceType::Hardware { device } => context.insert("name", &device),
|
|
_ => context.insert("name", &member.name),
|
|
};
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/bond-membership.network",
|
|
format!("50-bond-membership-{}.network", &member.name),
|
|
)?);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 3 Generate bridge netdev files
|
|
for interface in &pending_config.network.interfaces {
|
|
if let NetworkInterfaceType::Bridge { .. } = &interface.interface_type {
|
|
let mut context = Context::new();
|
|
context.insert("name", &interface.name);
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/create-bridge.netdev",
|
|
format!("30-create-bridge-{}.netdev", &interface.name),
|
|
)?);
|
|
|
|
// Create Membership files
|
|
for member in interface
|
|
.interface_type
|
|
.bridge_members(pending_config.clone())
|
|
{
|
|
let mut context = Context::new();
|
|
context.insert("bridge_name", &interface.name);
|
|
|
|
// if interface is a hardware interface then we want to use device instead
|
|
match member.interface_type {
|
|
NetworkInterfaceType::Hardware { device } => context.insert("name", &device),
|
|
_ => context.insert("name", &member.name),
|
|
};
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/bridge-membership.network",
|
|
format!("60-bridge-membership-{}.network", &member.name),
|
|
)?);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 4 Generate wireguard netdev files
|
|
for interface in &pending_config.vpn.wireguard.interfaces {
|
|
let mut context = Context::new();
|
|
context.insert("interface", &interface);
|
|
context.insert("peers", &interface.peers(pending_config.clone()));
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/create-wireguard.netdev",
|
|
format!("40-create-wireguard-{}.netdev", &interface.name),
|
|
)?);
|
|
}
|
|
|
|
// Step 5 Generate Addressing network files
|
|
for interface in &pending_config.network.interfaces {
|
|
let mut context = Context::new();
|
|
match &interface.interface_type {
|
|
NetworkInterfaceType::Hardware { device } => context.insert("name", &device),
|
|
_ => context.insert("name", &interface.name),
|
|
};
|
|
|
|
context.insert("interface", &interface);
|
|
|
|
// List of all vlans that have this interface as a parent
|
|
let mut vlans = Vec::new();
|
|
// TODO Use Backreferenceing instead of loop and if
|
|
for vlan in &pending_config.network.interfaces {
|
|
match &vlan.interface_type {
|
|
NetworkInterfaceType::Vlan { parent, .. } => {
|
|
if parent == &interface.name {
|
|
vlans.push(vlan.name.clone());
|
|
}
|
|
}
|
|
_ => (),
|
|
};
|
|
}
|
|
context.insert("vlans", &vlans);
|
|
|
|
// List all Static Routes for this interface
|
|
let mut static_routes = Vec::new();
|
|
// TODO Use Backreferenceing instead of loop and if
|
|
for static_route in &pending_config.network.static_routes {
|
|
if static_route.interface == interface.name {
|
|
static_routes.push(static_route);
|
|
}
|
|
}
|
|
context.insert("static_routes", &static_routes);
|
|
|
|
files.push(generate_config_file(
|
|
context,
|
|
"networkd/config-addressing.network",
|
|
format!("70-config-addressing-{}.network", &interface.name),
|
|
)?);
|
|
}
|
|
|
|
Ok(files)
|
|
}
|
|
|
|
fn generate_config_file(
|
|
context: Context,
|
|
template_name: &str,
|
|
file_name: String,
|
|
) -> Result<File, ApplyError> {
|
|
match templates::TEMPLATES.render(template_name, &context) {
|
|
Ok(s) => Ok(File {
|
|
name: file_name,
|
|
content: s,
|
|
}),
|
|
Err(e) => {
|
|
error!("Error: {}", e);
|
|
let mut cause = e.source();
|
|
while let Some(e) = cause {
|
|
error!("Reason: {}", e);
|
|
cause = e.source();
|
|
}
|
|
return Err(ApplyError::TemplateError(e));
|
|
}
|
|
}
|
|
}
|