diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d061e910f..961825975 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v2 - uses: Swatinem/rust-cache@v2 - name: Run Rita and Rita Exit tests - run: RUST_TEST_THREADS=1 cargo test --verbose --all + run: cargo test --verbose --all rustfmt: needs: check runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 379070ac9..300166b20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -446,7 +446,6 @@ dependencies = [ "crypto_box", "env_logger", "get_if_addrs", - "lazy_static", "log", "oping", "rand", @@ -3044,7 +3043,6 @@ dependencies = [ "hex-literal", "ipnetwork", "jemallocator", - "lazy_static", "log", "openssl", "openssl-probe", diff --git a/althea_kernel_interface/src/babel.rs b/althea_kernel_interface/src/babel.rs index 9cf877160..821a46532 100644 --- a/althea_kernel_interface/src/babel.rs +++ b/althea_kernel_interface/src/babel.rs @@ -1,7 +1,5 @@ -use crate::KernelInterface; +use crate::run_command; -impl dyn KernelInterface { - pub fn restart_babel(&self) { - let _res = self.run_command("/etc/init.d/babeld", &["restart"]); - } +pub fn restart_babel() { + let _res = run_command("/etc/init.d/babeld", &["restart"]); } diff --git a/althea_kernel_interface/src/bridge_tools.rs b/althea_kernel_interface/src/bridge_tools.rs index 1dd331d73..81ce463a2 100644 --- a/althea_kernel_interface/src/bridge_tools.rs +++ b/althea_kernel_interface/src/bridge_tools.rs @@ -1,15 +1,13 @@ //! Simple helper functions for brctl -use super::KernelInterface; +use crate::run_command; use crate::KernelInterfaceError as Error; use std::process::Output; -impl dyn KernelInterface { - pub fn add_if_to_bridge(&self, br: &str, iface: &str) -> Result { - self.run_command("brctl", &["addif", br, iface]) - } +pub fn add_if_to_bridge(br: &str, iface: &str) -> Result { + run_command("brctl", &["addif", br, iface]) +} - pub fn del_if_from_bridge(&self, br: &str, iface: &str) -> Result { - self.run_command("brctl", &["delif", br, iface]) - } +pub fn del_if_from_bridge(br: &str, iface: &str) -> Result { + run_command("brctl", &["delif", br, iface]) } diff --git a/althea_kernel_interface/src/check_cron.rs b/althea_kernel_interface/src/check_cron.rs index ddfc32579..889fb0019 100644 --- a/althea_kernel_interface/src/check_cron.rs +++ b/althea_kernel_interface/src/check_cron.rs @@ -1,19 +1,16 @@ -use super::KernelInterface; use crate::KernelInterfaceError as Error; use std::process::{Command, Stdio}; -impl dyn KernelInterface { - /// Checks if the cron service is running and starts it if it's not - pub fn check_cron(&self) -> Result<(), Error> { - Command::new("/etc/init.d/cron") - .args(["enable"]) - .stdout(Stdio::piped()) - .output()?; - Command::new("/etc/init.d/cron") - .args(["start"]) - .stdout(Stdio::piped()) - .output()?; +/// Checks if the cron service is running and starts it if it's not +pub fn check_cron() -> Result<(), Error> { + Command::new("/etc/init.d/cron") + .args(["enable"]) + .stdout(Stdio::piped()) + .output()?; + Command::new("/etc/init.d/cron") + .args(["start"]) + .stdout(Stdio::piped()) + .output()?; - Ok(()) - } + Ok(()) } diff --git a/althea_kernel_interface/src/counter.rs b/althea_kernel_interface/src/counter.rs index cb26b0a84..c3c6a2900 100644 --- a/althea_kernel_interface/src/counter.rs +++ b/althea_kernel_interface/src/counter.rs @@ -1,5 +1,6 @@ -use super::KernelInterface; -use crate::KernelInterfaceError as Error; +use crate::iptables::add_iptables_rule; +use crate::netfilter::{does_nftables_exist, nft_init_counters}; +use crate::{run_command, KernelInterfaceError as Error}; use regex::Regex; use std::collections::HashMap; use std::net::IpAddr; @@ -131,406 +132,132 @@ add zxcv 1234:5678:9801:2345:6789:0123:4567:8902,wg0 packets 123456789 bytes 987 } } -impl dyn KernelInterface { - pub fn init_counter(&self, target: &FilterTarget) -> Result<(), Error> { - if self.does_nftables_exist() { - info!("Trying to init a counter!"); - self.nft_init_counters(target.set_name(), target.chain(), target.nft_interface())?; - } else { - self.run_command( - "ipset", - &[ - "create", - target.set_name(), - "hash:net,iface", - "family", - "inet6", - "counters", - ], - )?; - self.add_iptables_rule( - "ip6tables", - &[ - "-w", - "-I", - target.table(), - "1", - "-m", - "set", - "!", - "--match-set", - target.set_name(), - &format!("dst,{}", target.interface()), - "-j", - "SET", - "--add-set", - target.set_name(), - &format!("dst,{}", target.interface()), - ], - )?; - } - - Ok(()) +pub fn init_counter(target: &FilterTarget) -> Result<(), Error> { + if does_nftables_exist() { + info!("Trying to init a counter!"); + nft_init_counters(target.set_name(), target.chain(), target.nft_interface())?; + } else { + run_command( + "ipset", + &[ + "create", + target.set_name(), + "hash:net,iface", + "family", + "inet6", + "counters", + ], + )?; + add_iptables_rule( + "ip6tables", + &[ + "-w", + "-I", + target.table(), + "1", + "-m", + "set", + "!", + "--match-set", + target.set_name(), + &format!("dst,{}", target.interface()), + "-j", + "SET", + "--add-set", + target.set_name(), + &format!("dst,{}", target.interface()), + ], + )?; } - fn parse_nft_set_counters( - &self, - set_name: &str, - ) -> Result, Error> { - let mut ret_map = HashMap::new(); - let out = self.run_command("nft", &["list", "set", "inet", "fw4", set_name])?; - // flush the list immediately to not missing accounting for any bytes - self.run_command("nft", &["flush", "set", "inet", "fw4", set_name])?; - - let out = out.stdout; - let out = String::from_utf8(out).expect("fix command"); - for line in out.lines() { - if line.contains("packets") { - let ret = line.replace("elements = { ", ""); - let mut ret = ret.split_ascii_whitespace(); - - // line is in the form: - // ff02::1:6 . \"wg0\" counter packets 3 bytes 204, - - let ip_addr = IpAddr::from_str(ret.next().unwrap_or(""))?; - - ret.next(); - - let iface = ret.next(); - let iface = if let Some(iface) = iface { - iface.replace('\"', "") - } else { - return Err(Error::ParseError(format!( - "No interface to parse for counter string {:?}", - line - ))); - }; - - ret.next(); - ret.next(); - let packets: u64 = ret.next().unwrap_or("").parse()?; - - ret.next(); - let bytes: u64 = ret.next().unwrap_or("").replace(',', "").parse()?; - - let total_bytes = (packets * 40) + bytes; - - trace!( - "ipaddr, iface, packets, bytes, total: {:?}, {:?} {:?}, {:?}, {:?}", - ip_addr, - iface, - packets, - bytes, - total_bytes - ); - - ret_map.insert((ip_addr, iface), total_bytes); - } - } + Ok(()) +} - Ok(ret_map) - } +fn parse_nft_set_counters(set_name: &str) -> Result, Error> { + let mut ret_map = HashMap::new(); + let out = run_command("nft", &["list", "set", "inet", "fw4", set_name])?; + // flush the list immediately to not missing accounting for any bytes + run_command("nft", &["flush", "set", "inet", "fw4", set_name])?; + + let out = out.stdout; + let out = String::from_utf8(out).expect("fix command"); + for line in out.lines() { + if line.contains("packets") { + let ret = line.replace("elements = { ", ""); + let mut ret = ret.split_ascii_whitespace(); + + // line is in the form: + // ff02::1:6 . \"wg0\" counter packets 3 bytes 204, + + let ip_addr = IpAddr::from_str(ret.next().unwrap_or(""))?; + + ret.next(); + + let iface = ret.next(); + let iface = if let Some(iface) = iface { + iface.replace('\"', "") + } else { + return Err(Error::ParseError(format!( + "No interface to parse for counter string {:?}", + line + ))); + }; + + ret.next(); + ret.next(); + let packets: u64 = ret.next().unwrap_or("").parse()?; + + ret.next(); + let bytes: u64 = ret.next().unwrap_or("").replace(',', "").parse()?; + + let total_bytes = (packets * 40) + bytes; + + trace!( + "ipaddr, iface, packets, bytes, total: {:?}, {:?} {:?}, {:?}, {:?}", + ip_addr, + iface, + packets, + bytes, + total_bytes + ); - pub fn read_counters( - &self, - target: &FilterTarget, - ) -> Result, Error> { - if self.does_nftables_exist() { - self.parse_nft_set_counters(target.set_name()) - } else { - self.run_command( - "ipset", - &[ - "create", - &format!("tmp_{}", target.set_name()), - "hash:net,iface", - "family", - "inet6", - "counters", - ], - )?; - - self.run_command( - "ipset", - &[ - "swap", - &format!("tmp_{}", target.set_name()), - target.set_name(), - ], - )?; - - let output = - self.run_command("ipset", &["save", &format!("tmp_{}", target.set_name())])?; - let res = parse_ipset(&String::from_utf8(output.stdout)?); - trace!("ipset parsed into {:?}", res); - - self.run_command("ipset", &["destroy", &format!("tmp_{}", target.set_name())])?; - res + ret_map.insert((ip_addr, iface), total_bytes); } } -} -#[test] -fn test_init_counter() { - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - use crate::KI; - - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - if KI.get_kernel_is_v4()? { - match counter { - 1 => { - assert_eq!(program, "ipset"); - assert_eq!( - args, - vec![ - "create", - "rita_input", - "hash:net,iface", - "family", - "inet6", - "counters", - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - assert_eq!(program, "ip6tables"); - assert_eq!( - args, - vec![ - "-w", - "-C", - "INPUT", - "-m", - "set", - "!", - "--match-set", - "rita_input", - "dst,src", - "-j", - "SET", - "--add-set", - "rita_input", - "dst,src", - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - } else { - match counter { - 1 => { - assert_eq!(program, "nft"); - assert_eq!(args, &["-v"]); - Ok(Output { - stdout: b"nftables v1.0.1 (Fearless Fosdick #3)".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - assert_eq!(program, "nft"); - assert_eq!(args, &["list", "set", "inet", "fw4", "rita_input"]); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 3 => { - assert_eq!(program, "nft"); - assert_eq!( - args, - &[ - "add", - "set", - "inet", - "fw4", - "rita_input", - "{", - "type", - "ipv6_addr", - ".", - "ifname;", - "flags", - "dynamic;", - "counter;", - "size", - "65535;", - "}", - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 4 => { - assert_eq!(program, "nft"); - assert_eq!( - args, - &[ - "insert", - "rule", - "inet", - "fw4", - "input", - "ip6", - "daddr", - ".", - "meta", - "iifname", - "@rita_input", - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - } - })); - KI.init_counter(&FilterTarget::Input) - .expect("Unable to init counter"); + Ok(ret_map) } -#[test] -fn test_read_counters() { - use std::net::Ipv6Addr; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - use crate::KI; - - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - if KI.get_kernel_is_v4()? { - match counter { - 1 => { - assert_eq!(program, "ipset"); - assert_eq!( - args, - vec![ - "create", - "tmp_rita_input", - "hash:net,iface", - "family", - "inet6", - "counters", - ] - ); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - assert_eq!(program, "ipset"); - assert_eq!(args, vec!["swap", "tmp_rita_input", "rita_input"]); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 3 => { - assert_eq!(program, "ipset"); - assert_eq!(args, vec!["save", "tmp_rita_input"]); - Ok(Output { - stdout: b" - add xxx fd00::dead:beef,wg42 packets 111 bytes 222 - " - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 4 => { - assert_eq!(program, "ipset"); - assert_eq!(args, vec!["destroy", "tmp_rita_input"]); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - } else { - match counter { - 1 => { - assert_eq!(program, "nft"); - assert_eq!(args, &["-v"]); - Ok(Output { - stdout: b"nftables v1.0.1 (Fearless Fosdick #3)".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - assert_eq!(program, "nft"); - assert_eq!(args, &["list", "set", "inet", "fw4", "rita_input"]); - Ok(Output { - stdout: b" - fd00::dead:beef . \"wg42\" counter packets 111 bytes 222 - " - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 3 => { - assert_eq!(program, "nft"); - assert_eq!(args, &["flush", "set", "inet", "fw4", "rita_input"]); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - } - })); - - let result = KI - .read_counters(&FilterTarget::Input) - .expect("Unable to read values"); - assert_eq!(result.len(), 1); - - let value = result - .get(&( - IpAddr::V6(Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0xdead, 0xbeef)), - "wg42".into(), - )) - .expect("Unable to find key"); - assert_eq!(value, &(222u64 + 111u64 * 40)); + +pub fn read_counters(target: &FilterTarget) -> Result, Error> { + if does_nftables_exist() { + parse_nft_set_counters(target.set_name()) + } else { + run_command( + "ipset", + &[ + "create", + &format!("tmp_{}", target.set_name()), + "hash:net,iface", + "family", + "inet6", + "counters", + ], + )?; + + run_command( + "ipset", + &[ + "swap", + &format!("tmp_{}", target.set_name()), + target.set_name(), + ], + )?; + + let output = run_command("ipset", &["save", &format!("tmp_{}", target.set_name())])?; + let res = parse_ipset(&String::from_utf8(output.stdout)?); + trace!("ipset parsed into {:?}", res); + + run_command("ipset", &["destroy", &format!("tmp_{}", target.set_name())])?; + res + } } diff --git a/althea_kernel_interface/src/create_wg_key.rs b/althea_kernel_interface/src/create_wg_key.rs index ff107a8ca..a2cb1342b 100644 --- a/althea_kernel_interface/src/create_wg_key.rs +++ b/althea_kernel_interface/src/create_wg_key.rs @@ -1,4 +1,3 @@ -use super::KernelInterface; use crate::KernelInterfaceError as Error; use althea_types::wg_key::WgKey; use std::fs::File; @@ -13,45 +12,43 @@ pub struct WgKeypair { pub private: WgKey, } -impl dyn KernelInterface { - pub fn create_wg_key(&self, path: &Path, private_key: &WgKey) -> Result<(), Error> { - trace!("Overwriting old private key file"); - let mut priv_key_file = File::create(path)?; - write!(priv_key_file, "{private_key}")?; - Ok(()) - } - - pub fn create_wg_keypair(&self) -> Result { - let genkey = Command::new("wg") - .args(["genkey"]) - .stdout(Stdio::piped()) - .output() - .expect("Are you sure wireguard is installed on this device?"); - - let mut pubkey = Command::new("wg") - .args(["pubkey"]) - .stdout(Stdio::piped()) - .stdin(Stdio::piped()) - .spawn() - .expect("Are you sure wireguard is installed on this device?"); - - pubkey - .stdin - .as_mut() - .unwrap() - .write_all(&genkey.stdout) - .expect("Failure generating wg keypair!"); - let output = pubkey.wait_with_output().unwrap(); - - let mut privkey_str = String::from_utf8(genkey.stdout)?; - let mut pubkey_str = String::from_utf8(output.stdout)?; - - privkey_str.truncate(44); - pubkey_str.truncate(44); - - let private = WgKey::from_str(&privkey_str).unwrap(); - let public = WgKey::from_str(&pubkey_str).unwrap(); - - Ok(WgKeypair { public, private }) - } +pub fn create_wg_key(path: &Path, private_key: &WgKey) -> Result<(), Error> { + trace!("Overwriting old private key file"); + let mut priv_key_file = File::create(path)?; + write!(priv_key_file, "{private_key}")?; + Ok(()) +} + +pub fn create_wg_keypair() -> Result { + let genkey = Command::new("wg") + .args(["genkey"]) + .stdout(Stdio::piped()) + .output() + .expect("Are you sure wireguard is installed on this device?"); + + let mut pubkey = Command::new("wg") + .args(["pubkey"]) + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .spawn() + .expect("Are you sure wireguard is installed on this device?"); + + pubkey + .stdin + .as_mut() + .unwrap() + .write_all(&genkey.stdout) + .expect("Failure generating wg keypair!"); + let output = pubkey.wait_with_output().unwrap(); + + let mut privkey_str = String::from_utf8(genkey.stdout)?; + let mut pubkey_str = String::from_utf8(output.stdout)?; + + privkey_str.truncate(44); + pubkey_str.truncate(44); + + let private = WgKey::from_str(&privkey_str).unwrap(); + let public = WgKey::from_str(&pubkey_str).unwrap(); + + Ok(WgKeypair { public, private }) } diff --git a/althea_kernel_interface/src/delete_tunnel.rs b/althea_kernel_interface/src/delete_tunnel.rs deleted file mode 100644 index 1607a0aad..000000000 --- a/althea_kernel_interface/src/delete_tunnel.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::KernelInterface; -use crate::KernelInterfaceError as Error; - -impl dyn KernelInterface { - pub fn delete_tunnel(&self, interface: &str) -> Result<(), Error> { - let output = self.run_command("ip", &["link", "del", interface])?; - if !output.stderr.is_empty() { - return Err(Error::RuntimeError(format!( - "received error deleting wireguard interface: {}", - String::from_utf8(output.stderr)? - ))); - } - Ok(()) - } -} - -#[test] -fn test_delete_tunnel_linux() { - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - use crate::KI; - - let ip_args = &["link", "del", "wg1"]; - - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, ip_args); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); - KI.delete_tunnel(&String::from("wg1")).unwrap(); -} diff --git a/althea_kernel_interface/src/dns.rs b/althea_kernel_interface/src/dns.rs index f73ccbeb4..b48d462b2 100644 --- a/althea_kernel_interface/src/dns.rs +++ b/althea_kernel_interface/src/dns.rs @@ -1,34 +1,30 @@ -use super::KernelInterface; - use std::fs::File; use std::io::Error; use std::io::Read; use std::net::IpAddr; -impl dyn KernelInterface { - /// Gets a list of IP addresses of nameservers from /etc/resolv.conf, may be v6, v4 or both - /// generally ignores malformed lines but produces IO errors - pub fn get_resolv_servers(&self) -> Result, Error> { - let mut f = File::open("/etc/resolv.conf")?; - let mut contents = String::new(); - f.read_to_string(&mut contents)?; +/// Gets a list of IP addresses of nameservers from /etc/resolv.conf, may be v6, v4 or both +/// generally ignores malformed lines but produces IO errors +pub fn get_resolv_servers() -> Result, Error> { + let mut f = File::open("/etc/resolv.conf")?; + let mut contents = String::new(); + f.read_to_string(&mut contents)?; - let mut res = Vec::new(); - for line in contents.lines() { - if line.starts_with("nameserver") { - let mut nameserver = line.split_whitespace(); - nameserver.next(); - match nameserver.next() { - Some(ip) => match ip.parse() { - Ok(addr) => res.push(addr), - Err(e) => { - warn!("Could not parse /etc/resolv.conf ip {:?} with {:?}", ip, e) - } - }, - None => warn!("Invalid /etc/resolv.conf!"), - } + let mut res = Vec::new(); + for line in contents.lines() { + if line.starts_with("nameserver") { + let mut nameserver = line.split_whitespace(); + nameserver.next(); + match nameserver.next() { + Some(ip) => match ip.parse() { + Ok(addr) => res.push(addr), + Err(e) => { + warn!("Could not parse /etc/resolv.conf ip {:?} with {:?}", ip, e) + } + }, + None => warn!("Invalid /etc/resolv.conf!"), } } - Ok(res) } + Ok(res) } diff --git a/althea_kernel_interface/src/exit_client_tunnel.rs b/althea_kernel_interface/src/exit_client_tunnel.rs index 2c63863a8..cf575daa3 100644 --- a/althea_kernel_interface/src/exit_client_tunnel.rs +++ b/althea_kernel_interface/src/exit_client_tunnel.rs @@ -1,5 +1,13 @@ -use super::KernelInterface; use crate::hardware_info::{get_kernel_version, parse_kernel_version}; +use crate::iptables::add_iptables_rule; +use crate::link_local_tools::{get_global_device_ip_v4, get_link_local_device_ip}; +use crate::netfilter::{ + delete_reject_rule, does_nftables_exist, init_nat_chain, insert_reject_rule, + set_nft_lan_fwd_rule, +}; +use crate::run_command; +use crate::setup_wg_if::get_peers; +use crate::traffic_control::set_codel_shaping; use crate::{open_tunnel::to_wg_local, KernelInterfaceError as Error}; use althea_types::WgKey; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; @@ -32,264 +40,260 @@ pub struct ClientExitTunnelConfig { pub user_specified_speed: Option, } -impl dyn KernelInterface { - pub fn get_kernel_is_v4(&self) -> Result { - let (_, system_kernel_version) = parse_kernel_version(get_kernel_version()?)?; - Ok(system_kernel_version.starts_with("4.")) - } +pub fn get_kernel_is_v4() -> Result { + let (_, system_kernel_version) = parse_kernel_version(get_kernel_version()?)?; + Ok(system_kernel_version.starts_with("4.")) +} - pub fn set_client_exit_tunnel_config( - &self, - args: ClientExitTunnelConfig, - local_mesh: Option, - ) -> Result<(), Error> { - self.run_command( - "wg", - &[ - "set", - "wg_exit", - "listen-port", - &args.listen_port.to_string(), - "private-key", - &args.private_key_path, - "peer", - &args.pubkey.to_string(), - "endpoint", - &format!("[{}]:{}", args.endpoint.ip(), args.endpoint.port()), - "allowed-ips", - "0.0.0.0/0, ::/0", - "persistent-keepalive", - "5", - ], - )?; +pub fn set_client_exit_tunnel_config( + args: ClientExitTunnelConfig, + local_mesh: Option, +) -> Result<(), Error> { + run_command( + "wg", + &[ + "set", + "wg_exit", + "listen-port", + &args.listen_port.to_string(), + "private-key", + &args.private_key_path, + "peer", + &args.pubkey.to_string(), + "endpoint", + &format!("[{}]:{}", args.endpoint.ip(), args.endpoint.port()), + "allowed-ips", + "0.0.0.0/0, ::/0", + "persistent-keepalive", + "5", + ], + )?; - // we only want one peer on this link, technically that one peer is multihomed - // via babel, but it has the same key so it's the same 'peer' from wireguard's - // perspective, if we don't do this we'll end up with multiple exits on the same - // tunnel - for i in self.get_peers("wg_exit")? { - if i != args.pubkey { - self.run_command("wg", &["set", "wg_exit", "peer", &format!("{i}"), "remove"])?; - } + // we only want one peer on this link, technically that one peer is multihomed + // via babel, but it has the same key so it's the same 'peer' from wireguard's + // perspective, if we don't do this we'll end up with multiple exits on the same + // tunnel + for i in get_peers("wg_exit")? { + if i != args.pubkey { + run_command("wg", &["set", "wg_exit", "peer", &format!("{i}"), "remove"])?; } + } - let prev_ip: Result = self.get_global_device_ip_v4("wg_exit"); - - match prev_ip { - Ok(prev_ip) => { - if prev_ip != args.local_ip { - self.run_command( - "ip", - &[ - "address", - "delete", - &format!("{}/{}", prev_ip, args.netmask), - "dev", - "wg_exit", - ], - )?; + let prev_ip: Result = get_global_device_ip_v4("wg_exit"); - self.run_command( - "ip", - &[ - "address", - "add", - &format!("{}/{}", args.local_ip, args.netmask), - "dev", - "wg_exit", - ], - )?; - } - } - Err(e) => { - warn!("Finding wg exit's current IP returned {}", e); - self.run_command( + match prev_ip { + Ok(prev_ip) => { + if prev_ip != args.local_ip { + run_command( "ip", &[ "address", - "add", - &format!("{}/{}", args.local_ip, args.netmask), + "delete", + &format!("{}/{}", prev_ip, args.netmask), "dev", "wg_exit", ], )?; - } - } - // If wg_exit does not have a link local addr, set one up - if self.get_link_local_device_ip("wg_exit").is_err() { - if let Some(mesh) = local_mesh { - if let Err(e) = self.run_command( + run_command( "ip", &[ "address", "add", - &format!("{}/64", to_wg_local(&mesh)), + &format!("{}/{}", args.local_ip, args.netmask), "dev", "wg_exit", ], - ) { - error!("IPV6 ERROR: Unable to set link local for wg_exit: {:?}", e); - } - } else { - error!("IPV6 ERRROR: No mesh ip, unable to set link local for wg_exit"); + )?; } } - - let output = self.run_command("ip", &["link", "set", "dev", "wg_exit", "mtu", "1340"])?; - if !output.stderr.is_empty() { - return Err(Error::RuntimeError(format!( - "received error adding wg link: {}", - String::from_utf8(output.stderr)? - ))); + Err(e) => { + warn!("Finding wg exit's current IP returned {}", e); + run_command( + "ip", + &[ + "address", + "add", + &format!("{}/{}", args.local_ip, args.netmask), + "dev", + "wg_exit", + ], + )?; } + } - let output = self.run_command("ip", &["link", "set", "dev", "wg_exit", "up"])?; - if !output.stderr.is_empty() { - return Err(Error::RuntimeError(format!( - "received error setting wg interface up: {}", - String::from_utf8(output.stderr)? - ))); + // If wg_exit does not have a link local addr, set one up + if get_link_local_device_ip("wg_exit").is_err() { + if let Some(mesh) = local_mesh { + if let Err(e) = run_command( + "ip", + &[ + "address", + "add", + &format!("{}/64", to_wg_local(&mesh)), + "dev", + "wg_exit", + ], + ) { + error!("IPV6 ERROR: Unable to set link local for wg_exit: {:?}", e); + } + } else { + error!("IPV6 ERRROR: No mesh ip, unable to set link local for wg_exit"); } + } - let _res = self.set_codel_shaping("br-lan", args.user_specified_speed); + let output = run_command("ip", &["link", "set", "dev", "wg_exit", "mtu", "1340"])?; + if !output.stderr.is_empty() { + return Err(Error::RuntimeError(format!( + "received error adding wg link: {}", + String::from_utf8(output.stderr)? + ))); + } - Ok(()) + let output = run_command("ip", &["link", "set", "dev", "wg_exit", "up"])?; + if !output.stderr.is_empty() { + return Err(Error::RuntimeError(format!( + "received error setting wg interface up: {}", + String::from_utf8(output.stderr)? + ))); } - pub fn set_route_to_tunnel(&self, gateway: &IpAddr) -> Result<(), Error> { - if let Err(e) = self.run_command("ip", &["route", "del", "default"]) { - warn!("Failed to delete default route {:?}", e); - } + let _res = set_codel_shaping("br-lan", args.user_specified_speed); - let output = self.run_command( - "ip", - &[ - "route", - "add", - "default", - "via", - &gateway.to_string(), - "dev", - "wg_exit", - ], - )?; - if !output.stderr.is_empty() { - return Err(Error::RuntimeError(format!( - "received error setting ip route: {}", - String::from_utf8(output.stderr)? - ))); - } + Ok(()) +} - Ok(()) +pub fn set_route_to_tunnel(gateway: &IpAddr) -> Result<(), Error> { + if let Err(e) = run_command("ip", &["route", "del", "default"]) { + warn!("Failed to delete default route {:?}", e); } - pub fn set_ipv6_route_to_tunnel(&self) -> Result<(), Error> { - // Remove current default route - if let Err(e) = self.run_command("ip", &["-6", "route", "del", "default"]) { - warn!("Failed to delete default ip6 route {:?}", e); - } - // Set new default route - let output = - self.run_command("ip", &["-6", "route", "add", "default", "dev", "wg_exit"])?; - if !output.stderr.is_empty() { - error!("IPV6 ERROR: Unable to set ip -6 default route"); - return Err(Error::RuntimeError(format!( - "received error setting ip -6 route: {}", - String::from_utf8(output.stderr)? - ))); - } - Ok(()) + let output = run_command( + "ip", + &[ + "route", + "add", + "default", + "via", + &gateway.to_string(), + "dev", + "wg_exit", + ], + )?; + if !output.stderr.is_empty() { + return Err(Error::RuntimeError(format!( + "received error setting ip route: {}", + String::from_utf8(output.stderr)? + ))); } - /// Adds nat rules for lan client, these act within the structure - /// of the openwrt rules which themselves create a few requirements - /// (such as saying that zone-lan-forward shoul jump to the accept table) - /// the nat rule here is very general, note the lack of restriction based - /// on incoming interface or ip, this is intentional, as it allows - /// the phone clients over in light_client_manager to function using these - /// same rules. It may be advisable in the future to split them up into - /// individual nat entires for each option - pub fn create_client_nat_rules(&self) -> Result<(), Error> { - let use_iptables = !self.does_nftables_exist(); + Ok(()) +} - if use_iptables { - self.add_iptables_rule( - "iptables", - &[ - "-t", - "nat", - "-A", - "POSTROUTING", - "-o", - "wg_exit", - "-j", - "MASQUERADE", - ], - )?; - self.add_iptables_rule("iptables", &["-A", "zone_lan_forward", "-j", "ACCEPT"])?; - } else { - self.init_nat_chain("wg_exit")?; - self.set_nft_lan_fwd_rule()?; - } +pub fn set_ipv6_route_to_tunnel() -> Result<(), Error> { + // Remove current default route + if let Err(e) = run_command("ip", &["-6", "route", "del", "default"]) { + warn!("Failed to delete default ip6 route {:?}", e); + } + // Set new default route + let output = run_command("ip", &["-6", "route", "add", "default", "dev", "wg_exit"])?; + if !output.stderr.is_empty() { + error!("IPV6 ERROR: Unable to set ip -6 default route"); + return Err(Error::RuntimeError(format!( + "received error setting ip -6 route: {}", + String::from_utf8(output.stderr)? + ))); + } + Ok(()) +} - // Set mtu - if use_iptables { - self.add_iptables_rule( - "iptables", - &[ - "-I", - "FORWARD", - "-p", - "tcp", - "--tcp-flags", - "SYN,RST", - "SYN", - "-j", - "TCPMSS", - "--clamp-mss-to-pmtu", //should be the same as --set-mss 1300 - ], - )?; +/// Adds nat rules for lan client, these act within the structure +/// of the openwrt rules which themselves create a few requirements +/// (such as saying that zone-lan-forward shoul jump to the accept table) +/// the nat rule here is very general, note the lack of restriction based +/// on incoming interface or ip, this is intentional, as it allows +/// the phone clients over in light_client_manager to function using these +/// same rules. It may be advisable in the future to split them up into +/// individual nat entires for each option +pub fn create_client_nat_rules() -> Result<(), Error> { + let use_iptables = !does_nftables_exist(); - //ipv6 support - self.add_iptables_rule( - "ip6tables", - &[ - "-I", - "FORWARD", - "-p", - "tcp", - "--tcp-flags", - "SYN,RST", - "SYN", - "-j", - "TCPMSS", - "--clamp-mss-to-pmtu", //should be the same as --set-mss 1300 - ], - )?; - } + if use_iptables { + add_iptables_rule( + "iptables", + &[ + "-t", + "nat", + "-A", + "POSTROUTING", + "-o", + "wg_exit", + "-j", + "MASQUERADE", + ], + )?; + add_iptables_rule("iptables", &["-A", "zone_lan_forward", "-j", "ACCEPT"])?; + } else { + init_nat_chain("wg_exit")?; + set_nft_lan_fwd_rule()?; + } - Ok(()) + // Set mtu + if use_iptables { + add_iptables_rule( + "iptables", + &[ + "-I", + "FORWARD", + "-p", + "tcp", + "--tcp-flags", + "SYN,RST", + "SYN", + "-j", + "TCPMSS", + "--clamp-mss-to-pmtu", //should be the same as --set-mss 1300 + ], + )?; + + //ipv6 support + add_iptables_rule( + "ip6tables", + &[ + "-I", + "FORWARD", + "-p", + "tcp", + "--tcp-flags", + "SYN,RST", + "SYN", + "-j", + "TCPMSS", + "--clamp-mss-to-pmtu", //should be the same as --set-mss 1300 + ], + )?; } - /// blocks the client nat by inserting a blocker in the start of the special lan forwarding - /// table created by openwrt. - pub fn block_client_nat(&self) -> Result<(), Error> { - if !self.does_nftables_exist() { - self.add_iptables_rule("iptables", &["-I", "zone_lan_forward", "-j", "REJECT"])?; - } else { - self.insert_reject_rule()?; - } - Ok(()) + Ok(()) +} + +/// blocks the client nat by inserting a blocker in the start of the special lan forwarding +/// table created by openwrt. +pub fn block_client_nat() -> Result<(), Error> { + if !does_nftables_exist() { + add_iptables_rule("iptables", &["-I", "zone_lan_forward", "-j", "REJECT"])?; + } else { + insert_reject_rule()?; } + Ok(()) +} - /// Removes the block created by block_client_nat() will fail if not run after that command - pub fn restore_client_nat(&self) -> Result<(), Error> { - if !self.does_nftables_exist() { - self.add_iptables_rule("iptables", &["-D", "zone_lan_forward", "-j", "REJECT"])?; - } else { - self.delete_reject_rule()?; - } - Ok(()) +/// Removes the block created by block_client_nat() will fail if not run after that command +pub fn restore_client_nat() -> Result<(), Error> { + if !does_nftables_exist() { + add_iptables_rule("iptables", &["-D", "zone_lan_forward", "-j", "REJECT"])?; + } else { + delete_reject_rule()?; } + Ok(()) } diff --git a/althea_kernel_interface/src/exit_server_tunnel.rs b/althea_kernel_interface/src/exit_server_tunnel.rs index 2f34c069f..4cff058ad 100644 --- a/althea_kernel_interface/src/exit_server_tunnel.rs +++ b/althea_kernel_interface/src/exit_server_tunnel.rs @@ -1,5 +1,10 @@ -use super::{KernelInterface, KernelInterfaceError}; +use super::KernelInterfaceError; +use crate::iptables::add_iptables_rule; +use crate::netfilter::{does_nftables_exist, init_nat_chain, insert_nft_exit_forward_rules}; use crate::open_tunnel::to_wg_local; +use crate::run_command; +use crate::setup_wg_if::get_peers; +use crate::traffic_control::{create_root_classful_limit, has_limit}; use althea_types::WgKey; use ipnetwork::IpNetwork; use std::collections::HashSet; @@ -15,316 +20,305 @@ pub struct ExitClient { pub port: u16, } -impl dyn KernelInterface { - // This function sets up the exit config and returns the updated list of tc filter handles - pub fn set_exit_wg_config( - &self, - clients: &HashSet, - listen_port: u16, - private_key_path: &str, - if_name: &str, - ) -> Result<(), Error> { - let command = "wg".to_string(); - - let mut args = vec![ - "set".into(), - if_name.into(), - "listen-port".into(), - format!("{listen_port}"), - "private-key".into(), - private_key_path.to_string(), - ]; - - let mut client_pubkeys = HashSet::new(); - - for c in clients.iter() { - // For the allowed IPs, we appends the clients internal ip as well - // as the client ipv6 assigned ip and add this to wireguards allowed ips - // internet_ipv6 is already in the form of ",.." - let i_ipv6 = &c.internet_ipv6; - let mut allowed_ips = c.internal_ip.to_string().to_owned(); - if let Some(i_ipv6) = i_ipv6 { - allowed_ips.push(','); - allowed_ips.push_str(&i_ipv6.to_string()); - } - - args.push("peer".into()); - args.push(format!("{}", c.public_key)); - args.push("endpoint".into()); - args.push(format!("[{}]:{}", c.mesh_ip, c.port)); - args.push("allowed-ips".into()); - args.push(allowed_ips); - - client_pubkeys.insert(c.public_key); +// This function sets up the exit config and returns the updated list of tc filter handles +pub fn set_exit_wg_config( + clients: &HashSet, + listen_port: u16, + private_key_path: &str, + if_name: &str, +) -> Result<(), Error> { + let command = "wg".to_string(); + + let mut args = vec![ + "set".into(), + if_name.into(), + "listen-port".into(), + format!("{listen_port}"), + "private-key".into(), + private_key_path.to_string(), + ]; + + let mut client_pubkeys = HashSet::new(); + + for c in clients.iter() { + // For the allowed IPs, we appends the clients internal ip as well + // as the client ipv6 assigned ip and add this to wireguards allowed ips + // internet_ipv6 is already in the form of ",.." + let i_ipv6 = &c.internet_ipv6; + let mut allowed_ips = c.internal_ip.to_string().to_owned(); + if let Some(i_ipv6) = i_ipv6 { + allowed_ips.push(','); + allowed_ips.push_str(&i_ipv6.to_string()); } - let arg_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + args.push("peer".into()); + args.push(format!("{}", c.public_key)); + args.push("endpoint".into()); + args.push(format!("[{}]:{}", c.mesh_ip, c.port)); + args.push("allowed-ips".into()); + args.push(allowed_ips); - self.run_command(&command, &arg_str[..])?; + client_pubkeys.insert(c.public_key); + } - let wg_peers = self.get_peers(if_name)?; - info!("{} has {} peers", if_name, wg_peers.len()); - for i in wg_peers { - if !client_pubkeys.contains(&i) { - warn!("Removing no longer authorized peer {}", i); - self.run_command("wg", &["set", if_name, "peer", &format!("{i}"), "remove"])?; - } - } + let arg_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); - Ok(()) - } + run_command(&command, &arg_str[..])?; - /// This function adds a route for each client ipv4 subnet to the routing table - /// this works on the premise of smallest prefix first routing meaning that we can assign - /// ip route 172.168.0.1/16 to wg_exit_v2 and then individually add /32 routes to wg_exit_v1 - /// and this will produce the same routing outcomes with many less routes than adding individual routes - /// on both - pub fn setup_individual_client_routes( - &self, - client_internal_ip: IpAddr, - exit_internal_v4: IpAddr, - interface: &str, - ) { - let mut interface_cloned = interface.to_string(); - interface_cloned.push(' '); - // Setup ipv4 route - // 1.) Select all ipv4 routes with 'client_internal_ip'. This gives us all routes with wg_exit and wg_exit_v2 for the ip - // THere should be only one route - // 2.) Does the route contain 'interface'? Yes? we continue - // 3.) No? That means either no route exists or there is a route with the other interface name - // 4.) Delete route, add new route with the correct interface - let output = self - .run_command("ip", &["route", "show", &client_internal_ip.to_string()]) - .expect("Fix command"); - let route = String::from_utf8(output.stdout).unwrap(); - if !route.contains(&interface_cloned) { - self.run_command("ip", &["route", "del", &client_internal_ip.to_string()]) - .expect("Fix command"); - - self.run_command( - "ip", - &[ - "route", - "add", - &client_internal_ip.to_string(), - "dev", - interface, - "src", - &exit_internal_v4.to_string(), - ], - ) - .expect("Fix command"); + let wg_peers = get_peers(if_name)?; + info!("{} has {} peers", if_name, wg_peers.len()); + for i in wg_peers { + if !client_pubkeys.contains(&i) { + warn!("Removing no longer authorized peer {}", i); + run_command("wg", &["set", if_name, "peer", &format!("{i}"), "remove"])?; } } - /// this function performs the teardown step of setup_indvidual_client_routes, when a router upgrades - /// to beta20 or later this function checks for and deltes the rules - pub fn teardown_individual_client_routes(&self, client_internal_ip: IpAddr) { - let output = self - .run_command("ip", &["route", "show", &client_internal_ip.to_string()]) - .expect("Fix command"); - let route = String::from_utf8(output.stdout).unwrap(); - if !route.is_empty() { - self.run_command("ip", &["route", "del", &client_internal_ip.to_string()]) - .expect("Fix command"); - } - } + Ok(()) +} - /// Performs the one time startup tasks for the rita_exit clients loop - pub fn one_time_exit_setup( - &self, - local_v4: Option<(IpAddr, u8)>, - external_v6: Option<(IpAddr, u8)>, - exit_mesh: IpAddr, - interface: &str, - enable_enforcement: bool, - ) -> Result<(), Error> { - if let Some((local_ip_v4, netmask_v4)) = local_v4 { - // sanity checking - assert!(local_ip_v4.is_ipv4()); - assert!(netmask_v4 < 32); - - let _output = self.run_command( - "ip", - &[ - "address", - "add", - &format!("{local_ip_v4}/{netmask_v4}"), - "dev", - interface, - ], - )?; - } +/// This function adds a route for each client ipv4 subnet to the routing table +/// this works on the premise of smallest prefix first routing meaning that we can assign +/// ip route 172.168.0.1/16 to wg_exit_v2 and then individually add /32 routes to wg_exit_v1 +/// and this will produce the same routing outcomes with many less routes than adding individual routes +/// on both +pub fn setup_individual_client_routes( + client_internal_ip: IpAddr, + exit_internal_v4: IpAddr, + interface: &str, +) { + let mut interface_cloned = interface.to_string(); + interface_cloned.push(' '); + // Setup ipv4 route + // 1.) Select all ipv4 routes with 'client_internal_ip'. This gives us all routes with wg_exit and wg_exit_v2 for the ip + // THere should be only one route + // 2.) Does the route contain 'interface'? Yes? we continue + // 3.) No? That means either no route exists or there is a route with the other interface name + // 4.) Delete route, add new route with the correct interface + let output = run_command("ip", &["route", "show", &client_internal_ip.to_string()]) + .expect("Fix command"); + let route = String::from_utf8(output.stdout).unwrap(); + if !route.contains(&interface_cloned) { + run_command("ip", &["route", "del", &client_internal_ip.to_string()]).expect("Fix command"); + + run_command( + "ip", + &[ + "route", + "add", + &client_internal_ip.to_string(), + "dev", + interface, + "src", + &exit_internal_v4.to_string(), + ], + ) + .expect("Fix command"); + } +} - // setup ipv6 if provided2602:FBAD:10::/45 - if let Some((external_ip_v6, netmask_v6)) = external_v6 { - // sanity checking - assert!(external_ip_v6.is_ipv6()); - assert!(netmask_v6 < 128); +/// this function performs the teardown step of setup_indvidual_client_routes, when a router upgrades +/// to beta20 or later this function checks for and deltes the rules +pub fn teardown_individual_client_routes(client_internal_ip: IpAddr) { + let output = run_command("ip", &["route", "show", &client_internal_ip.to_string()]) + .expect("Fix command"); + let route = String::from_utf8(output.stdout).unwrap(); + if !route.is_empty() { + run_command("ip", &["route", "del", &client_internal_ip.to_string()]).expect("Fix command"); + } +} - let _output = self.run_command( - "ip", - &[ - "address", - "add", - &format!("{external_ip_v6}/{netmask_v6}"), - "dev", - interface, - ], - )?; - } +/// Performs the one time startup tasks for the rita_exit clients loop +pub fn one_time_exit_setup( + local_v4: Option<(IpAddr, u8)>, + external_v6: Option<(IpAddr, u8)>, + exit_mesh: IpAddr, + interface: &str, + enable_enforcement: bool, +) -> Result<(), Error> { + if let Some((local_ip_v4, netmask_v4)) = local_v4 { + // sanity checking + assert!(local_ip_v4.is_ipv4()); + assert!(netmask_v4 < 32); + + let _output = run_command( + "ip", + &[ + "address", + "add", + &format!("{local_ip_v4}/{netmask_v4}"), + "dev", + interface, + ], + )?; + } - // Set up link local mesh ip in wg_exit as fe80 + rest of mesh ip of exit - let local_link = to_wg_local(&exit_mesh); + // setup ipv6 if provided2602:FBAD:10::/45 + if let Some((external_ip_v6, netmask_v6)) = external_v6 { + // sanity checking + assert!(external_ip_v6.is_ipv6()); + assert!(netmask_v6 < 128); - let _output = self.run_command( + let _output = run_command( "ip", &[ "address", "add", - &format!("{local_link}/64"), + &format!("{external_ip_v6}/{netmask_v6}"), "dev", interface, ], )?; + } - let output = self.run_command("ip", &["link", "set", "dev", interface, "mtu", "1500"])?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error adding wg link: {}", - String::from_utf8(output.stderr)? - ))); - } + // Set up link local mesh ip in wg_exit as fe80 + rest of mesh ip of exit + let local_link = to_wg_local(&exit_mesh); + + let _output = run_command( + "ip", + &[ + "address", + "add", + &format!("{local_link}/64"), + "dev", + interface, + ], + )?; + + let output = run_command("ip", &["link", "set", "dev", interface, "mtu", "1500"])?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error adding wg link: {}", + String::from_utf8(output.stderr)? + ))); + } - let output = self.run_command("ip", &["link", "set", "dev", interface, "up"])?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error setting wg interface up: {}", - String::from_utf8(output.stderr)? - ))); - } + let output = run_command("ip", &["link", "set", "dev", interface, "up"])?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error setting wg interface up: {}", + String::from_utf8(output.stderr)? + ))); + } - // this creates the root classful htb limit for which we will make - // subclasses to enforce payment - if !self.has_limit(interface)? && enable_enforcement { - info!( - "Setting up root HTB qdisc for interface: {:?}, this should only run once", - interface - ); - self.create_root_classful_limit(interface) - .expect("Failed to setup root HTB qdisc!"); - } + // this creates the root classful htb limit for which we will make + // subclasses to enforce payment + if !has_limit(interface)? && enable_enforcement { + info!( + "Setting up root HTB qdisc for interface: {:?}, this should only run once", + interface + ); + create_root_classful_limit(interface).expect("Failed to setup root HTB qdisc!"); + } + + Ok(()) +} - Ok(()) +/// Sets up the natting rules for forwarding ipv4 and ipv6 traffic +pub fn setup_nat( + external_interface: &str, + interface: &str, + external_v6: Option<(IpAddr, u8)>, +) -> Result<(), Error> { + // nat masquerade on exit + if !does_nftables_exist() { + add_iptables_rule( + "iptables", + &[ + "-w", + "-t", + "nat", + "-A", + "POSTROUTING", + "-o", + external_interface, + "-j", + "MASQUERADE", + ], + )?; + } else { + init_nat_chain(external_interface)?; } - /// Sets up the natting rules for forwarding ipv4 and ipv6 traffic - pub fn setup_nat( - &self, - external_interface: &str, - interface: &str, - external_v6: Option<(IpAddr, u8)>, - ) -> Result<(), Error> { - // nat masquerade on exit - if !self.does_nftables_exist() { - self.add_iptables_rule( - "iptables", - &[ - "-w", - "-t", - "nat", - "-A", - "POSTROUTING", - "-o", - external_interface, - "-j", - "MASQUERADE", - ], - )?; - } else { - self.init_nat_chain(external_interface)?; - } + // Add v4 and v6 forward rules wg_exit <-> ex_nic + if !does_nftables_exist() { + // v4 wg_exit -> ex_nic + add_iptables_rule( + "iptables", + &[ + "-w", + "-t", + "filter", + "-A", + "FORWARD", + "-o", + external_interface, + "-i", + interface, + "-j", + "ACCEPT", + ], + )?; - // Add v4 and v6 forward rules wg_exit <-> ex_nic - if !self.does_nftables_exist() { - // v4 wg_exit -> ex_nic - self.add_iptables_rule( - "iptables", - &[ - "-w", - "-t", - "filter", - "-A", - "FORWARD", - "-o", - external_interface, - "-i", - interface, - "-j", - "ACCEPT", - ], - )?; + // v4 ex_nic -> interface + add_iptables_rule( + "iptables", + &[ + "-w", + "-t", + "filter", + "-A", + "FORWARD", + "-o", + interface, + "-i", + external_interface, + "-m", + "state", + "--state", + "RELATED,ESTABLISHED", + "-j", + "ACCEPT", + ], + )?; - // v4 ex_nic -> interface - self.add_iptables_rule( - "iptables", - &[ - "-w", - "-t", - "filter", - "-A", - "FORWARD", - "-o", - interface, - "-i", - external_interface, - "-m", - "state", - "--state", - "RELATED,ESTABLISHED", - "-j", - "ACCEPT", - ], - )?; + // Add iptable routes between wg_exit and the external nic + add_iptables_rule( + "ip6tables", + &[ + "-A", + "FORWARD", + "-i", + interface, + "-o", + external_interface, + "-j", + "ACCEPT", + ], + )?; - // Add iptable routes between wg_exit and the external nic - self.add_iptables_rule( + if let Some((external_ip_v6, netmask_v6)) = external_v6 { + add_iptables_rule( "ip6tables", &[ "-A", "FORWARD", + "-d", + &format!("{}/{}", external_ip_v6, netmask_v6), "-i", - interface, - "-o", external_interface, + "-o", + interface, "-j", "ACCEPT", ], )?; - - if let Some((external_ip_v6, netmask_v6)) = external_v6 { - self.add_iptables_rule( - "ip6tables", - &[ - "-A", - "FORWARD", - "-d", - &format!("{}/{}", external_ip_v6, netmask_v6), - "-i", - external_interface, - "-o", - interface, - "-j", - "ACCEPT", - ], - )?; - } - } else { - self.insert_nft_exit_forward_rules(interface, external_interface, external_v6)?; } - - Ok(()) + } else { + insert_nft_exit_forward_rules(interface, external_interface, external_v6)?; } + + Ok(()) } #[test] diff --git a/althea_kernel_interface/src/fs_sync.rs b/althea_kernel_interface/src/fs_sync.rs index 4f8e4c19b..170266127 100644 --- a/althea_kernel_interface/src/fs_sync.rs +++ b/althea_kernel_interface/src/fs_sync.rs @@ -1,12 +1,10 @@ -use super::KernelInterface; +use crate::run_command; use crate::KernelInterfaceError as Error; -impl dyn KernelInterface { - /// Performs a full filesystem sync by running the sync command. - /// If there are any outstanding writes they will be flushed to the disk - /// Currently used because UBIFS devices seem to have issues - pub fn fs_sync(&self) -> Result<(), Error> { - self.run_command("sync", &[])?; - Ok(()) - } +/// Performs a full filesystem sync by running the sync command. +/// If there are any outstanding writes they will be flushed to the disk +/// Currently used because UBIFS devices seem to have issues +pub fn fs_sync() -> Result<(), Error> { + run_command("sync", &[])?; + Ok(()) } diff --git a/althea_kernel_interface/src/get_neighbors.rs b/althea_kernel_interface/src/get_neighbors.rs index 1c71d9c21..df0726d13 100644 --- a/althea_kernel_interface/src/get_neighbors.rs +++ b/althea_kernel_interface/src/get_neighbors.rs @@ -1,68 +1,55 @@ -use crate::KernelInterface; -use crate::KernelInterfaceError as Error; +use crate::{run_command, KernelInterfaceError as Error}; use regex::Regex; -use std::collections::HashSet; use std::net::IpAddr; +use std::process::Output; use std::str::FromStr; -impl dyn KernelInterface { - /// Returns a vector of neighbors reachable over layer 2, giving IP address of each. - /// Implemented with `ip neighbor` on Linux. - pub fn get_neighbors(&self) -> Result, Error> { - let output = self.run_command("ip", &["neigh"])?; - trace!("Got {:?} from `ip neighbor`", output); +/// Internal testing function to parse the output of `ip neighbor` on Linux. +fn parse_neighbors_internal(output: Output) -> Result, Error> { + let mut vec = Vec::new(); - let mut vec = Vec::new(); - - lazy_static! { - static ref RE: Regex = - Regex::new(r"(\S*).*dev (\S*).*lladdr (\S*).*(REACHABLE|STALE|DELAY)") - .expect("Unable to compile regular expression"); - } - for caps in RE.captures_iter(&String::from_utf8(output.stdout)?) { - trace!("Regex captured {:?}", caps); - - vec.push((IpAddr::from_str(&caps[1])?, caps[2].to_string())); - } - trace!("Got neighbors {:?}", vec); - Ok(vec) + lazy_static! { + static ref RE: Regex = + Regex::new(r"(\S*).*dev (\S*).*lladdr (\S*).*(REACHABLE|STALE|DELAY)") + .expect("Unable to compile regular expression"); } + for caps in RE.captures_iter(&String::from_utf8(output.stdout)?) { + trace!("Regex captured {:?}", caps); - pub fn trigger_neighbor_disc(&self, interfaces: &HashSet) -> Result<(), Error> { - for interface in interfaces.iter() { - self.run_command("ping6", &["-c1", "-I", interface, "ff02::1"])?; - } - Ok(()) + vec.push((IpAddr::from_str(&caps[1])?, caps[2].to_string())); } + trace!("Got neighbors {:?}", vec); + Ok(vec) +} + +/// Returns a vector of neighbors reachable over layer 2, giving IP address of each. +/// Implemented with `ip neighbor` on Linux. +pub fn get_neighbors() -> Result, Error> { + let output = run_command("ip", &["neigh"])?; + trace!("Got {:?} from `ip neighbor`", output); + parse_neighbors_internal(output) } #[test] fn test_get_neighbors_linux() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; use std::process::ExitStatus; use std::process::Output; - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, &["neigh"]); - - Ok(Output { - stdout: b"10.0.2.2 dev eth0 lladdr 00:00:00:aa:00:03 STALE + let output = Output { + stdout: b"10.0.2.2 dev eth0 lladdr 00:00:00:aa:00:03 STALE 10.0.0.2 dev eth0 FAILED 10.0.1.2 dev eth0 lladdr 00:00:00:aa:00:05 REACHABLE 2001::2 dev eth0 lladdr 00:00:00:aa:00:56 REACHABLE fe80::7459:8eff:fe98:81 dev eth0 lladdr 76:59:8e:98:00:81 STALE fe80::433:25ff:fe8c:e1ea dev eth0 lladdr 1a:32:06:78:05:0a STALE 2001::2 dev eth0 FAILED" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); + .to_vec(), + stderr: b"".to_vec(), + status: ExitStatus::from_raw(0), + }; - let addresses = KI.get_neighbors().unwrap(); + let addresses = parse_neighbors_internal(output).unwrap(); //assert_eq!(format!("{}", addresses[0].0), "00-00-00-aa-00-03"); assert_eq!(addresses[0].0.to_string(), "10.0.2.2"); diff --git a/althea_kernel_interface/src/hardware_info.rs b/althea_kernel_interface/src/hardware_info.rs index 9c53c9ac1..79a0b2e81 100644 --- a/althea_kernel_interface/src/hardware_info.rs +++ b/althea_kernel_interface/src/hardware_info.rs @@ -1,6 +1,8 @@ use crate::file_io::get_lines; +use crate::is_openwrt::is_openwrt; +use crate::manipulate_uci::get_uci_var; +use crate::run_command; use crate::KernelInterfaceError as Error; -use crate::KI; use althea_types::extract_wifi_station_data; use althea_types::extract_wifi_survey_data; use althea_types::ConntrackInfo; @@ -404,10 +406,10 @@ fn get_conntrack_info() -> Option { /// Device names are in the form wlan0, wlan1 etc fn parse_wifi_device_names() -> Result, Error> { // We parse /etc/config/wireless which is an openwrt config. We return an error if not openwrt - if KI.is_openwrt() { + if is_openwrt() { let mut ret = Vec::new(); - let lines = KI.run_command("uci", &["show", "wireless"])?; + let lines = run_command("uci", &["show", "wireless"])?; let lines: Vec<&str> = from_utf8(&lines.stdout)?.lines().collect(); // trying to get lines 'wireless.default_radio1.ifname='wlan1'' @@ -490,7 +492,7 @@ fn get_wifi_station_info(dev: &str) -> Vec { pub fn get_radio_ssid(radio: &str) -> Option { let radio = radio.replace("wlan", "radio"); let path = format!("wireless.default_{radio}.ssid"); - match KI.get_uci_var(&path) { + match get_uci_var(&path) { Ok(a) => Some(a), Err(e) => { error!("Unable to get radio ssid for radio: {} with {:?}", radio, e); @@ -506,7 +508,7 @@ pub fn get_radio_ssid(radio: &str) -> Option { pub fn get_radio_enabled(radio: &str) -> Option { let radio = radio.replace("wlan", "radio"); let path = format!("wireless.{radio}.disabled"); - match KI.get_uci_var(&path) { + match get_uci_var(&path) { // if disabled flag is set to '0' in config, radio is enabled so we return true and vice versa Ok(a) => Some(a.contains('0')), Err(e) => { @@ -527,7 +529,7 @@ pub fn get_radio_enabled(radio: &str) -> Option { pub fn get_radio_channel(radio: &str) -> Option { let radio = radio.replace("wlan", "radio"); let path = format!("wireless.{radio}.channel"); - match KI.get_uci_var(&path) { + match get_uci_var(&path) { Ok(a) => match a.parse::() { Ok(a) => Some(a), Err(e) => { diff --git a/althea_kernel_interface/src/interface_tools.rs b/althea_kernel_interface/src/interface_tools.rs index c2e120c09..e4fd90329 100644 --- a/althea_kernel_interface/src/interface_tools.rs +++ b/althea_kernel_interface/src/interface_tools.rs @@ -1,5 +1,6 @@ use crate::file_io::get_lines; -use crate::KernelInterface; +use crate::netns::get_namespace; +use crate::run_command; use crate::KernelInterfaceError as Error; use althea_types::InterfaceUsageStats; use regex::Regex; @@ -19,102 +20,99 @@ fn get_helper(input: Option<&&str>) -> Result { } } -impl dyn KernelInterface { - /// Gets usage data from all interfaces from /proc/net/dev, note that for wireguard interfaces - /// updating the interface on the fly (like we do with wg_exit) will reset the usage - /// counter on the wireguard side, but not on in proc which this code pulls from - pub fn get_per_interface_usage(&self) -> Result, Error> { - let lines = get_lines("/proc/net/dev")?; - // all lines represent an interface, except the first line which is a header - let mut lines = lines.iter(); - // skip the first and second lines - lines.next(); - lines.next(); - let mut ret = Vec::new(); - for line in lines { - println!("line ins {}", line); - let parts: Vec<&str> = line.split_ascii_whitespace().collect(); - ret.push(InterfaceUsageStats { - interface_name: get_helper(parts.first())?.trim_end_matches(':').to_string(), - recieve_bytes: get_helper(parts.get(1))?.parse()?, - recieve_packets: get_helper(parts.get(2))?.parse()?, - recieve_errors: get_helper(parts.get(3))?.parse()?, - recieve_dropped: get_helper(parts.get(4))?.parse()?, - recieve_fifo_errors: get_helper(parts.get(5))?.parse()?, - recieve_frame_errors: get_helper(parts.get(6))?.parse()?, - recieve_multicast_erorrs: get_helper(parts.get(8))?.parse()?, - transmit_bytes: get_helper(parts.get(9))?.parse()?, - transmit_packets: get_helper(parts.get(10))?.parse()?, - transmit_errors: get_helper(parts.get(11))?.parse()?, - transmit_fifo_errors: get_helper(parts.get(12))?.parse()?, - transmit_collission_erorrs: get_helper(parts.get(13))?.parse()?, - tranmist_carrier_errors: get_helper(parts.get(14))?.parse()?, - }) - } - Ok(ret) +/// Gets usage data from all interfaces from /proc/net/dev, note that for wireguard interfaces +/// updating the interface on the fly (like we do with wg_exit) will reset the usage +/// counter on the wireguard side, but not on in proc which this code pulls from +pub fn get_per_interface_usage() -> Result, Error> { + let lines = get_lines("/proc/net/dev")?; + // all lines represent an interface, except the first line which is a header + let mut lines = lines.iter(); + // skip the first and second lines + lines.next(); + lines.next(); + let mut ret = Vec::new(); + for line in lines { + println!("line ins {}", line); + let parts: Vec<&str> = line.split_ascii_whitespace().collect(); + ret.push(InterfaceUsageStats { + interface_name: get_helper(parts.first())?.trim_end_matches(':').to_string(), + recieve_bytes: get_helper(parts.get(1))?.parse()?, + recieve_packets: get_helper(parts.get(2))?.parse()?, + recieve_errors: get_helper(parts.get(3))?.parse()?, + recieve_dropped: get_helper(parts.get(4))?.parse()?, + recieve_fifo_errors: get_helper(parts.get(5))?.parse()?, + recieve_frame_errors: get_helper(parts.get(6))?.parse()?, + recieve_multicast_erorrs: get_helper(parts.get(8))?.parse()?, + transmit_bytes: get_helper(parts.get(9))?.parse()?, + transmit_packets: get_helper(parts.get(10))?.parse()?, + transmit_errors: get_helper(parts.get(11))?.parse()?, + transmit_fifo_errors: get_helper(parts.get(12))?.parse()?, + transmit_collission_erorrs: get_helper(parts.get(13))?.parse()?, + tranmist_carrier_errors: get_helper(parts.get(14))?.parse()?, + }) } + Ok(ret) +} - /// Returns all existing interfaces - pub fn get_interfaces(&self) -> Result, Error> { - let links = read_dir("/sys/class/net/")?; - - let mut vec = Vec::new(); - for dir in links { - let dir = dir?; - if dir.path().is_dir() { - // this could fail if the interface contains any characters - // not allowed in unicode. I don't think the kernel allows - // this in the first place - vec.push(dir.file_name().to_str().unwrap().to_string()); - } +/// Returns all existing interfaces +pub fn get_interfaces() -> Result, Error> { + let links = read_dir("/sys/class/net/")?; + + let mut vec = Vec::new(); + for dir in links { + let dir = dir?; + if dir.path().is_dir() { + // this could fail if the interface contains any characters + // not allowed in unicode. I don't think the kernel allows + // this in the first place + vec.push(dir.file_name().to_str().unwrap().to_string()); } - - trace!("interfaces: {:?}", vec); - Ok(vec) } - pub fn ifindex_to_interface_name(&self, ifindex: usize) -> Result { - for interface in self.get_interfaces()? { - let found_ifindex = self.get_ifindex(&interface)?; - if ifindex == found_ifindex { - return Ok(interface); - } - } - Err(Error::NoInterfaceError(ifindex.to_string())) - } + trace!("interfaces: {:?}", vec); + Ok(vec) +} - /// Deletes an named interface - pub fn del_interface(&self, name: &str) -> Result<(), Error> { - self.run_command("ip", &["link", "del", "dev", name])?; - Ok(()) +pub fn ifindex_to_interface_name(ifindex: usize) -> Result { + for interface in get_interfaces()? { + let found_ifindex = get_ifindex(&interface)?; + if ifindex == found_ifindex { + return Ok(interface); + } } + Err(Error::NoInterfaceError(ifindex.to_string())) +} - pub fn iface_status(&self, iface: &str) -> Result { - // cat so we can mock - let output = self.run_command("cat", &[&format!("/sys/class/net/{iface}/operstate")])?; +/// Deletes an named interface +pub fn del_interface(name: &str) -> Result<(), Error> { + run_command("ip", &["link", "del", "dev", name])?; + Ok(()) +} - let output = from_utf8(&output.stdout)?; +pub fn iface_status(iface: &str) -> Result { + // cat so we can mock + let output = run_command("cat", &[&format!("/sys/class/net/{iface}/operstate")])?; - Ok(output.trim_end().to_string()) - } + let output = from_utf8(&output.stdout)?; - pub fn get_wg_remote_ip(&self, name: &str) -> Result { - let output = self.run_command("wg", &["show", name, "endpoints"])?; - let stdout = String::from_utf8(output.stdout)?; + Ok(output.trim_end().to_string()) +} - lazy_static! { - static ref RE: Regex = Regex::new(r"(?:([0-9a-f:]+)%)|(?:([0-9\.]+):)") - .expect("Unable to compile regular expression"); - } - let cap = RE.captures(&stdout); - - match cap { - Some(cap) => { - let ip_str = match cap.get(1) { - // ipv6 - Some(cap) => cap.as_str(), - None => { - match cap.get(2) { +/// Internal testing function for get_wg_remote_ip +fn get_wg_remote_ip_internal(stdout: String, name: String) -> Result { + lazy_static! { + static ref RE: Regex = Regex::new(r"(?:([0-9a-f:]+)%)|(?:([0-9\.]+):)") + .expect("Unable to compile regular expression"); + } + let cap = RE.captures(&stdout); + + match cap { + Some(cap) => { + let ip_str = match cap.get(1) { + // ipv6 + Some(cap) => cap.as_str(), + None => { + match cap.get(2) { Some(cap) => { // ipv4 cap.as_str() @@ -125,236 +123,204 @@ impl dyn KernelInterface { ))) } } - } - }; + } + }; - Ok(ip_str.parse()?) - } - None => Err(Error::RuntimeError(format!( - "Cannot parse `wg show {name} endpoints` output, got {stdout}, nothing captured" - ))), + Ok(ip_str.parse()?) } + None => Err(Error::RuntimeError(format!( + "Cannot parse `wg show {name} endpoints` output, got {stdout}, nothing captured" + ))), } +} - /// Gets all the IPv4 addresses from an interface and returns the address and it's netmask - /// as a tuple. - pub fn get_ip_from_iface(&self, name: &str) -> Result, Error> { - let output = self.run_command("ip", &["address", "show", "dev", name])?; - let stdout = String::from_utf8(output.stdout)?; +pub fn get_wg_remote_ip(name: &str) -> Result { + let output = run_command("wg", &["show", name, "endpoints"])?; + let stdout = String::from_utf8(output.stdout)?; + get_wg_remote_ip_internal(stdout, name.to_string()) +} - lazy_static! { - static ref RE: Regex = Regex::new(r"((\d){1,3}\.){3}(\d){1,3}/(\d){1,3}") - .expect("Unable to compile regular expression"); - } +/// Internal utility function for testing get_ip_from_iface +fn get_ip_from_iface_internal(stdout: String) -> Result, Error> { + lazy_static! { + static ref RE: Regex = Regex::new(r"((\d){1,3}\.){3}(\d){1,3}/(\d){1,3}") + .expect("Unable to compile regular expression"); + } - let mut ret = Vec::new(); - for line in stdout.lines() { - let cap = RE.captures(line); - // we captured something on this line - if let Some(cap) = cap { - // flatten drops the 'none' values in this array - for ip_cap in cap.iter().flatten() { - let mut split = ip_cap.as_str().split('/'); - let ip_str = split.next(); - let netmask = split.next(); - if let (Some(ip_str), Some(netmask)) = (ip_str, netmask) { - if let (Ok(parsed_ip), Ok(parsed_netmask)) = - (ip_str.parse(), netmask.parse()) - { - ret.push((parsed_ip, parsed_netmask)); - } + let mut ret = Vec::new(); + for line in stdout.lines() { + let cap = RE.captures(line); + // we captured something on this line + if let Some(cap) = cap { + // flatten drops the 'none' values in this array + for ip_cap in cap.iter().flatten() { + let mut split = ip_cap.as_str().split('/'); + let ip_str = split.next(); + let netmask = split.next(); + if let (Some(ip_str), Some(netmask)) = (ip_str, netmask) { + if let (Ok(parsed_ip), Ok(parsed_netmask)) = (ip_str.parse(), netmask.parse()) { + ret.push((parsed_ip, parsed_netmask)); } } } } - - Ok(ret) } - /// Gets all the IPv6 addresses from an interface and returns the address and it's netmask - /// as a tuple. - pub fn get_ipv6_from_iface(&self, name: &str) -> Result, Error> { - let output = self.run_command("ip", &["address", "show", "dev", name])?; - let stdout = String::from_utf8(output.stdout)?; + Ok(ret) +} - lazy_static! { - static ref RE: Regex = Regex::new(r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/(\d){1,3}").expect("Unable to compile regular expression"); - } +/// Gets all the IPv4 addresses from an interface and returns the address and it's netmask +/// as a tuple. +pub fn get_ip_from_iface(name: &str) -> Result, Error> { + let output = run_command("ip", &["address", "show", "dev", name])?; + let stdout = String::from_utf8(output.stdout)?; + get_ip_from_iface_internal(stdout) +} - let mut ret = Vec::new(); - for line in stdout.lines() { - let cap = RE.captures(line); - // we captured something on this line - if let Some(cap) = cap { - // flatten drops the 'none' values in this array - for ip_cap in cap.iter().flatten() { - let mut split = ip_cap.as_str().split('/'); - let ip_str = split.next(); - let netmask = split.next(); - if let (Some(ip_str), Some(netmask)) = (ip_str, netmask) { - if let (Ok(parsed_ip), Ok(parsed_netmask)) = - (ip_str.parse(), netmask.parse()) - { - ret.push((parsed_ip, parsed_netmask)); - } +/// Gets all the IPv6 addresses from an interface and returns the address and it's netmask +/// as a tuple. +pub fn get_ipv6_from_iface(name: &str) -> Result, Error> { + let output = run_command("ip", &["address", "show", "dev", name])?; + let stdout = String::from_utf8(output.stdout)?; + + lazy_static! { + static ref RE: Regex = Regex::new(r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/(\d){1,3}").expect("Unable to compile regular expression"); + } + + let mut ret = Vec::new(); + for line in stdout.lines() { + let cap = RE.captures(line); + // we captured something on this line + if let Some(cap) = cap { + // flatten drops the 'none' values in this array + for ip_cap in cap.iter().flatten() { + let mut split = ip_cap.as_str().split('/'); + let ip_str = split.next(); + let netmask = split.next(); + if let (Some(ip_str), Some(netmask)) = (ip_str, netmask) { + if let (Ok(parsed_ip), Ok(parsed_netmask)) = (ip_str.parse(), netmask.parse()) { + ret.push((parsed_ip, parsed_netmask)); } } } } + } + + Ok(ret) +} - Ok(ret) +/// calls iproute2 to set an interface up or down +pub fn set_if_up_down(if_name: &str, up_down: &str) -> Result<(), Error> { + let output = run_command("ip", &["link", "set", "dev", if_name, up_down])?; + if !output.stderr.is_empty() { + Err(Error::RuntimeError(format!( + "received error setting wg interface up: {}", + String::from_utf8(output.stderr)? + ))) + } else { + Ok(()) } +} - /// calls iproute2 to set an interface up or down - pub fn set_if_up_down(&self, if_name: &str, up_down: &str) -> Result<(), Error> { - let output = self.run_command("ip", &["link", "set", "dev", if_name, up_down])?; - if !output.stderr.is_empty() { - Err(Error::RuntimeError(format!( - "received error setting wg interface up: {}", - String::from_utf8(output.stderr)? - ))) - } else { - Ok(()) - } +/// Gets the mtu from an interface +pub fn get_mtu(if_name: &str) -> Result { + let lines = get_lines(&format!("/sys/class/net/{if_name}/mtu"))?; + if let Some(mtu) = lines.first() { + Ok(mtu.parse()?) + } else { + Err(Error::NoInterfaceError(if_name.to_string())) } +} - /// Gets the mtu from an interface - pub fn get_mtu(&self, if_name: &str) -> Result { - let lines = get_lines(&format!("/sys/class/net/{if_name}/mtu"))?; - if let Some(mtu) = lines.first() { - Ok(mtu.parse()?) +/// Gets the ifindex from an interface +pub fn get_ifindex(if_name: &str) -> Result { + if cfg!(feature = "integration_test") { + // ip netns exec n-1 cat /sys/class/net/veth-n-1-n-2/iflink + let ns = get_namespace().unwrap(); + let location = format!("/sys/class/net/{if_name}/ifindex"); + let index = run_command("ip", &["netns", "exec", &ns, "cat", &location])?; + + let index = match String::from_utf8(index.stdout) { + Ok(mut s) => { + //this outputs with an extra newline \n on the end which was messing up the next command + s.truncate(s.len() - 1); + s + } + Err(_) => panic!("Could not get index number!"), + }; + info!("location: {:?}, index {:?}", location, index); + + Ok(index.parse().unwrap()) + } else { + let lines = get_lines(&format!("/sys/class/net/{if_name}/ifindex"))?; + if let Some(ifindex) = lines.first() { + Ok(ifindex.parse()?) } else { Err(Error::NoInterfaceError(if_name.to_string())) } } +} - /// Gets the ifindex from an interface - pub fn get_ifindex(&self, if_name: &str) -> Result { - if cfg!(feature = "integration_test") { - // ip netns exec n-1 cat /sys/class/net/veth-n-1-n-2/iflink - let ns = self.get_namespace().unwrap(); - let location = format!("/sys/class/net/{if_name}/ifindex"); - let index = self - .run_command("ip", &["netns", "exec", &ns, "cat", &location]) - .unwrap(); - - let index = match String::from_utf8(index.stdout) { - Ok(mut s) => { - //this outputs with an extra newline \n on the end which was messing up the next command - s.truncate(s.len() - 1); - s - } - Err(_) => panic!("Could not get index number!"), - }; - info!("location: {:?}, index {:?}", location, index); - - Ok(index.parse().unwrap()) - } else { - let lines = get_lines(&format!("/sys/class/net/{if_name}/ifindex"))?; - if let Some(ifindex) = lines.first() { - Ok(ifindex.parse()?) - } else { - Err(Error::NoInterfaceError(if_name.to_string())) - } - } +/// Gets the iflink value from an interface. Physical interfaces have an ifindex and iflink that are +/// identical but if you have a virtual (say DSA) interface then this will be the physical interface name +pub fn get_iflink(if_name: &str) -> Result { + let lines = get_lines(&format!("/sys/class/net/{if_name}/iflink"))?; + if let Some(iflink) = lines.first() { + Ok(iflink.parse()?) + } else { + Err(Error::NoInterfaceError(if_name.to_string())) } +} - /// Gets the iflink value from an interface. Physical interfaces have an ifindex and iflink that are - /// identical but if you have a virtual (say DSA) interface then this will be the physical interface name - pub fn get_iflink(&self, if_name: &str) -> Result { - let lines = get_lines(&format!("/sys/class/net/{if_name}/iflink"))?; - if let Some(iflink) = lines.first() { - Ok(iflink.parse()?) - } else { - Err(Error::NoInterfaceError(if_name.to_string())) - } +/// Sets the mtu of an interface, if this interface is a DSA interface the +/// parent interface will be located and it's mtu increased as appropriate +pub fn set_mtu(if_name: &str, mtu: usize) -> Result<(), Error> { + let ifindex = get_ifindex(if_name)?; + let iflink = get_iflink(if_name)?; + // dsa interface detected, this is an interface controlled by an internal switch + // the parent interface (which has the ifindex value represented in iflink for the child interface) + // needs to have whatever the mtu is, plus room for VLAN headers + const DSA_VLAN_HEADER_SIZE: usize = 8; + if ifindex != iflink { + let parent_if_name = ifindex_to_interface_name(iflink)?; + set_mtu(&parent_if_name, mtu + DSA_VLAN_HEADER_SIZE)?; } - /// Sets the mtu of an interface, if this interface is a DSA interface the - /// parent interface will be located and it's mtu increased as appropriate - pub fn set_mtu(&self, if_name: &str, mtu: usize) -> Result<(), Error> { - let ifindex = self.get_ifindex(if_name)?; - let iflink = self.get_iflink(if_name)?; - // dsa interface detected, this is an interface controlled by an internal switch - // the parent interface (which has the ifindex value represented in iflink for the child interface) - // needs to have whatever the mtu is, plus room for VLAN headers - const DSA_VLAN_HEADER_SIZE: usize = 8; - if ifindex != iflink { - let parent_if_name = self.ifindex_to_interface_name(iflink)?; - self.set_mtu(&parent_if_name, mtu + DSA_VLAN_HEADER_SIZE)?; - } - - let output = self.run_command( - "ip", - &["link", "set", "dev", if_name, "mtu", &mtu.to_string()], - )?; - if !output.stderr.is_empty() { - Err(Error::RuntimeError(format!( - "received error setting interface mtu: {}", - String::from_utf8(output.stderr)? - ))) - } else { - Ok(()) - } + let output = run_command( + "ip", + &["link", "set", "dev", if_name, "mtu", &mtu.to_string()], + )?; + if !output.stderr.is_empty() { + Err(Error::RuntimeError(format!( + "received error setting interface mtu: {}", + String::from_utf8(output.stderr)? + ))) + } else { + Ok(()) } } #[test] fn test_get_wg_remote_ip() { - use crate::KI; - - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "wg"); - assert_eq!(args, &["show", "wg0", "endpoints"]); - Ok(Output { - stdout: b"fvLYbeMV+RYbzJEc4lNEPuK8ulva/5wcSJBz0W5t3hM= 71.8.186.226:60000\ -" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); + let stdout = "fvLYbeMV+RYbzJEc4lNEPuK8ulva/5wcSJBz0W5t3hM= 71.8.186.226:60000"; assert_eq!( - KI.get_wg_remote_ip("wg0").unwrap(), + get_wg_remote_ip_internal(stdout.to_string(), "eth8".to_string()).unwrap(), "71.8.186.226".parse::().unwrap() ); - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "wg"); - assert_eq!(args, &["show", "wg0", "endpoints"]); - Ok(Output{ - stdout: b"v5yFYZVfl98N/LRVDK3hbyt5/dK/00VnEGHRBikHHXs= [fe80::78e4:1cff:fe61:560d%veth-1-6]:60000\ -".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); + let stdout = + "v5yFYZVfl98N/LRVDK3hbyt5/dK/00VnEGHRBikHHXs= [fe80::78e4:1cff:fe61:560d%veth-1-6]:60000"; assert_eq!( - KI.get_wg_remote_ip("wg0").unwrap(), + get_wg_remote_ip_internal(stdout.to_string(), "eth8".to_string()).unwrap(), "fe80::78e4:1cff:fe61:560d".parse::().unwrap() ); } #[test] fn test_get_ip_addresses_linux() { - use crate::KI; - - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, &["address", "show", "dev", "eth8"]); - - Ok(Output { - stdout: b" + let stdout =" 13: eth8: mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:e0:4c:67:a1:57 brd ff:ff:ff:ff:ff:ff inet 192.168.1.203/32 scope global eth8 @@ -379,20 +345,14 @@ fn test_get_ip_addresses_linux() { valid_lft forever preferred_lft forever inet6 fe80::2e0:4cff:fe67:a157/64 scope link valid_lft forever preferred_lft forever - " - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); - - let interfaces = KI.get_ip_from_iface("eth8").unwrap(); + "; + + let interfaces = get_ip_from_iface_internal(stdout.to_string()).unwrap(); let val = ("192.168.1.203".parse().unwrap(), 32); assert!(interfaces.contains(&val)) } #[test] fn test_get_interface_usage() { - use crate::KI; - let _ = KI.get_per_interface_usage().unwrap(); + let _ = get_per_interface_usage().unwrap(); } diff --git a/althea_kernel_interface/src/ip_addr.rs b/althea_kernel_interface/src/ip_addr.rs index 0cd011049..3ee07e4c0 100644 --- a/althea_kernel_interface/src/ip_addr.rs +++ b/althea_kernel_interface/src/ip_addr.rs @@ -1,223 +1,161 @@ use crate::hardware_info::maybe_get_single_line_string; +use crate::interface_tools::get_ipv6_from_iface; use crate::open_tunnel::is_link_local; -use crate::KernelInterface; +use crate::run_command; use crate::KernelInterfaceError as Error; use ipnetwork::IpNetwork; use std::net::IpAddr; use std::net::Ipv4Addr; -impl dyn KernelInterface { - /// Returns a bool based on device state, "UP" or "DOWN", "UNKNOWN" is - /// interpreted as DOWN - pub fn is_iface_up(&self, dev: &str) -> Option { - let path = format!("/sys/class/net/{dev}"); - if let Some(is_up) = maybe_get_single_line_string(&format!("{path}/operstate")) { - let is_up = is_up.contains("up"); - Some(is_up) - } else { - None - } +/// Returns a bool based on device state, "UP" or "DOWN", "UNKNOWN" is +/// interpreted as DOWN +pub fn is_iface_up(dev: &str) -> Option { + let path = format!("/sys/class/net/{dev}"); + if let Some(is_up) = maybe_get_single_line_string(&format!("{path}/operstate")) { + let is_up = is_up.contains("up"); + Some(is_up) + } else { + None } +} - /// Adds an ipv4 address to a given interface, true is returned when - /// the ip is added, false if it is already there and Error if the interface - /// does not exist or some other error has occured - pub fn add_ipv4(&self, ip: Ipv4Addr, dev: &str) -> Result { - // upwrap here because it's ok if we panic when the system does not have 'ip' installed - let output = self - .run_command("ip", &["addr", "add", &format!("{ip}/32"), "dev", dev]) - .unwrap(); - // Get the first line, check if it has "file exists" - match String::from_utf8(output.stderr) { - Ok(stdout) => match stdout.lines().next() { - Some(line) => { - if line.contains("File exists") { - Ok(false) - } else { - Err(Error::RuntimeError(format!("Error setting ip {line}"))) - } +/// Adds an ipv4 address to a given interface, true is returned when +/// the ip is added, false if it is already there and Error if the interface +/// does not exist or some other error has occured +pub fn add_ipv4(ip: Ipv4Addr, dev: &str) -> Result { + let output = run_command("ip", &["addr", "add", &format!("{ip}/32"), "dev", dev])?; + // Get the first line, check if it has "file exists" + match String::from_utf8(output.stderr) { + Ok(stdout) => match stdout.lines().next() { + Some(line) => { + if line.contains("File exists") { + Ok(false) + } else { + Err(Error::RuntimeError(format!("Error setting ip {line}"))) } - None => Ok(true), - }, - Err(e) => Err(Error::RuntimeError(format!( - "Could not decode stderr from ip with {e:?}" - ))), - } - } - - /// After receiving an ipv6 addr from the exit, this function adds that ip - /// to br-lan. SLAAC takes this ip and assigns a /64 to hosts that connect - /// to the router - /// We take a /128 of this ipv6 addr and assign it to ourselves in wg_exit as our own ipv6 addr - pub fn setup_ipv6_slaac(&self, router_ipv6_str: IpNetwork) { - // Get all the v6 addrs on interface - let v6_addrs = match self.get_ipv6_from_iface("br-lan") { - Ok(a) => { - trace!("Our ip list on brlan looks like {:?}", a); - a - } - Err(e) => { - error!("IPV6 ERROR: Unable to parse ips from interface br-lan, didnt not setup slaac: {:?}", e); - return; - } - }; - - for (addr, netmask) in v6_addrs { - let net = IpNetwork::new(IpAddr::V6(addr), netmask) - .expect("Why did we get an invalid addr from kernel?"); - // slaac addr is already set - if net == router_ipv6_str { - break; } + None => Ok(true), + }, + Err(e) => Err(Error::RuntimeError(format!( + "Could not decode stderr from ip with {e:?}" + ))), + } +} - // Remove all previously set ipv6 addrs - if !is_link_local(IpAddr::V6(addr)) { - if let Err(e) = - self.run_command("ip", &["addr", "del", &net.to_string(), "dev", "br-lan"]) - { - error!( - "IPV6 Error: Why are not able to delete the addr {:?} Error {:?}", - net, e - ); - } - } +/// After receiving an ipv6 addr from the exit, this function adds that ip +/// to br-lan. SLAAC takes this ip and assigns a /64 to hosts that connect +/// to the router +/// We take a /128 of this ipv6 addr and assign it to ourselves in wg_exit as our own ipv6 addr +pub fn setup_ipv6_slaac(router_ipv6_str: IpNetwork) { + // Get all the v6 addrs on interface + let v6_addrs = match get_ipv6_from_iface("br-lan") { + Ok(a) => { + trace!("Our ip list on brlan looks like {:?}", a); + a } - - // Add the new ipv6 addr - if let Err(e) = self.run_command( - "ip", - &["addr", "add", &router_ipv6_str.to_string(), "dev", "br-lan"], - ) { - error!( - "IPV6 ERROR: WHy are we unalbe to add the new subnet {:?} Error: {:?}", - router_ipv6_str, e - ); + Err(e) => { + error!("IPV6 ERROR: Unable to parse ips from interface br-lan, didnt not setup slaac: {:?}", e); + return; + } + }; + + for (addr, netmask) in v6_addrs { + let net = IpNetwork::new(IpAddr::V6(addr), netmask) + .expect("Why did we get an invalid addr from kernel?"); + // slaac addr is already set + if net == router_ipv6_str { + break; } - // Do the same thing for wg_exit - let v6_addrs = match self.get_ipv6_from_iface("wg_exit") { - Ok(a) => { - trace!("Our ip list on wg_exit looks like {:?}", a); - a - } - Err(e) => { - error!("IPV6 ERROR: Unable to parse ips from interface wg_exit, didnt not setup ipv6 correctly: {:?}", e); - return; + // Remove all previously set ipv6 addrs + if !is_link_local(IpAddr::V6(addr)) { + if let Err(e) = run_command("ip", &["addr", "del", &net.to_string(), "dev", "br-lan"]) { + error!( + "IPV6 Error: Why are not able to delete the addr {:?} Error {:?}", + net, e + ); } - }; + } + } - for (addr, _netmask) in v6_addrs { - // slaac addr is already set - if addr == router_ipv6_str.ip() { - return; - } + // Add the new ipv6 addr + if let Err(e) = run_command( + "ip", + &["addr", "add", &router_ipv6_str.to_string(), "dev", "br-lan"], + ) { + error!( + "IPV6 ERROR: WHy are we unalbe to add the new subnet {:?} Error: {:?}", + router_ipv6_str, e + ); + } - // Remove all previously set ipv6 addrs - if !is_link_local(IpAddr::V6(addr)) { - if let Err(e) = - self.run_command("ip", &["addr", "del", &addr.to_string(), "dev", "wg_exit"]) - { - error!( - "IPV6 Error: Why are not able to delete the addr {:?} Error {:?}", - addr, e - ); - } - } + // Do the same thing for wg_exit + let v6_addrs = match get_ipv6_from_iface("wg_exit") { + Ok(a) => { + trace!("Our ip list on wg_exit looks like {:?}", a); + a + } + Err(e) => { + error!("IPV6 ERROR: Unable to parse ips from interface wg_exit, didnt not setup ipv6 correctly: {:?}", e); + return; } + }; - // Add the new ipv6 addr - if let Err(e) = self.run_command( - "ip", - &[ - "addr", - "add", - &router_ipv6_str.ip().to_string(), - "dev", - "wg_exit", - ], - ) { - error!( - "IPV6 ERROR: WHy are we unalbe to add the new ip {:?} Error: {:?}", - router_ipv6_str, e - ); + for (addr, _netmask) in v6_addrs { + // slaac addr is already set + if addr == router_ipv6_str.ip() { + return; } - } - /// Adds an ipv4 address to a given interface, true is returned when - /// the ip is added, false if it is already there and Error if the interface - /// does not exist or some other error has occured - pub fn add_ipv4_mask(&self, ip: Ipv4Addr, mask: u32, dev: &str) -> Result { - // upwrap here because it's ok if we panic when the system does not have 'ip' installed - let output = self - .run_command("ip", &["addr", "add", &format!("{ip}/{mask}"), "dev", dev]) - .unwrap(); - // Get the first line, check if it has "file exists" - match String::from_utf8(output.stderr) { - Ok(stdout) => match stdout.lines().next() { - Some(line) => { - if line.contains("File exists") { - Ok(false) - } else { - Err(Error::RuntimeError(format!("Error setting ip {line}"))) - } - } - None => Ok(true), - }, - Err(e) => Err(Error::RuntimeError(format!( - "Could not decode stderr from ip with {e:?}" - ))), + // Remove all previously set ipv6 addrs + if !is_link_local(IpAddr::V6(addr)) { + if let Err(e) = run_command("ip", &["addr", "del", &addr.to_string(), "dev", "wg_exit"]) + { + error!( + "IPV6 Error: Why are not able to delete the addr {:?} Error {:?}", + addr, e + ); + } } } -} - -#[test] -fn test_add_ipv4() { - use crate::KI; - - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, &["addr", "add", "192.168.31.2/32", "dev", "eth0"]); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"RTNETLINK answers: File exists".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); - let val = KI - .add_ipv4("192.168.31.2".parse().unwrap(), "eth0") - .expect("Failure to run ip test"); - assert!(!val); - - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, &["addr", "add", "192.168.31.2/32", "dev", "eth0"]); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"Cannot find device \"eth0\"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); - let val = KI.add_ipv4("192.168.31.2".parse().unwrap(), "eth0"); - assert!(val.is_err()); - - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, &["addr", "add", "192.168.31.2/32", "dev", "eth0"]); + // Add the new ipv6 addr + if let Err(e) = run_command( + "ip", + &[ + "addr", + "add", + &router_ipv6_str.ip().to_string(), + "dev", + "wg_exit", + ], + ) { + error!( + "IPV6 ERROR: WHy are we unalbe to add the new ip {:?} Error: {:?}", + router_ipv6_str, e + ); + } +} - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); - let val = KI - .add_ipv4("192.168.31.2".parse().unwrap(), "eth0") - .expect("Failure to run ip test"); - assert!(val); +/// Adds an ipv4 address to a given interface, true is returned when +/// the ip is added, false if it is already there and Error if the interface +/// does not exist or some other error has occured +pub fn add_ipv4_mask(ip: Ipv4Addr, mask: u32, dev: &str) -> Result { + let output = run_command("ip", &["addr", "add", &format!("{ip}/{mask}"), "dev", dev])?; + // Get the first line, check if it has "file exists" + match String::from_utf8(output.stderr) { + Ok(stdout) => match stdout.lines().next() { + Some(line) => { + if line.contains("File exists") { + Ok(false) + } else { + Err(Error::RuntimeError(format!("Error setting ip {line}"))) + } + } + None => Ok(true), + }, + Err(e) => Err(Error::RuntimeError(format!( + "Could not decode stderr from ip with {e:?}" + ))), + } } diff --git a/althea_kernel_interface/src/ip_neigh.rs b/althea_kernel_interface/src/ip_neigh.rs index e759396d5..991fb2a35 100644 --- a/althea_kernel_interface/src/ip_neigh.rs +++ b/althea_kernel_interface/src/ip_neigh.rs @@ -1,31 +1,29 @@ -use crate::KernelInterface; +use crate::run_command; use mac_address::MacAddress; use std::io::{Error, ErrorKind}; use std::net::IpAddr; -impl dyn KernelInterface { - /// Runs the ip neigh command via the Kernel interface lazy static and returns an error if it doesn't work - pub fn grab_ip_neigh(&self) -> Result, std::io::Error> { - info!("Sending ip neigh command to kernel"); - let res = self.run_command("ip", &["neigh"]); - match res { - Ok(output) => { - // Extra checking since output struct is ambigious on how it works - if !output.stdout.is_empty() { - let string_to_parse = String::from_utf8_lossy(&output.stdout).to_string(); - Ok(parse_ip_neigh(string_to_parse)) - } else { - Err(Error::new( - ErrorKind::Other, - "Empty ip neigh command. Failed".to_string(), - )) - } +/// Runs the ip neigh command via the Kernel interface lazy static and returns an error if it doesn't work +pub fn grab_ip_neigh() -> Result, std::io::Error> { + info!("Sending ip neigh command to kernel"); + let res = run_command("ip", &["neigh"]); + match res { + Ok(output) => { + // Extra checking since output struct is ambigious on how it works + if !output.stdout.is_empty() { + let string_to_parse = String::from_utf8_lossy(&output.stdout).to_string(); + Ok(parse_ip_neigh(string_to_parse)) + } else { + Err(Error::new( + ErrorKind::Other, + "Empty ip neigh command. Failed".to_string(), + )) } - Err(e) => Err(Error::new( - ErrorKind::Other, - format!("Unable to grab ip neigh from router. Failed with error {e:?}"), - )), } + Err(e) => Err(Error::new( + ErrorKind::Other, + format!("Unable to grab ip neigh from router. Failed with error {e:?}"), + )), } } diff --git a/althea_kernel_interface/src/ip_route.rs b/althea_kernel_interface/src/ip_route.rs index 8e14e00f4..ef8d11c32 100644 --- a/althea_kernel_interface/src/ip_route.rs +++ b/althea_kernel_interface/src/ip_route.rs @@ -1,4 +1,4 @@ -use crate::KernelInterface; +use crate::run_command; use crate::KernelInterfaceError as Error; use althea_types::FromStr; use std::fmt::Display; @@ -190,141 +190,118 @@ impl Display for IpRoute { } } -impl dyn KernelInterface { - /// Gets the default route, returns Error if the command fails and None - /// if no default route is set - pub fn get_default_route(&self) -> Result, Error> { - let output = self.run_command("ip", &["route", "list", "default"])?; - - let stdout = String::from_utf8(output.stdout).unwrap(); - // return the first valid default route that correctly parses into a route - // there can be multiple default routes with different metrics but ip is kind - // enough to always put the lowest metric (aka the 'best' one) to the top - for line in stdout.lines() { - match line.parse() { - Ok(route) => { - if let IpRoute::DefaultRoute(r) = route { - return Ok(Some(r)); - } +/// Internal unit testing function for get_default_route() +fn get_default_route_internal(stdout: String) -> Result, Error> { + // return the first valid default route that correctly parses into a route + // there can be multiple default routes with different metrics but ip is kind + // enough to always put the lowest metric (aka the 'best' one) to the top + for line in stdout.lines() { + match line.parse() { + Ok(route) => { + if let IpRoute::DefaultRoute(r) = route { + return Ok(Some(r)); } - Err(e) => error!("Failed to parse route! {:?}", e), } + Err(e) => error!("Failed to parse route! {:?}", e), } - Ok(None) } + Ok(None) +} - pub fn set_route(&self, to: &IpRoute) -> Result<(), Error> { - let to = to.to_string(); - let to: Vec<&str> = to.split_whitespace().collect(); - let mut def_route = vec!["route", "add"]; - def_route.extend(to); - self.run_command("ip", &def_route)?; - Ok(()) - } +/// Gets the default route, returns Error if the command fails and None +/// if no default route is set +pub fn get_default_route() -> Result, Error> { + let output = run_command("ip", &["route", "list", "default"])?; - /// Updates the settings default route, returns true if an edit to the settings has been performed - pub fn update_settings_route( - &self, - settings_default_route: &mut Option, - ) -> Result { - let def_route = match self.get_default_route()? { - Some(route) => route, - None => return Ok(false), - }; - if !def_route.is_althea_default_route() { - // update the default route if default route is not wg exit - *settings_default_route = Some(def_route); - Ok(true) - } else { - Ok(false) - } + let stdout = String::from_utf8(output.stdout).unwrap(); + get_default_route_internal(stdout) +} + +pub fn set_route(to: &IpRoute) -> Result<(), Error> { + let to = to.to_string(); + let to: Vec<&str> = to.split_whitespace().collect(); + let mut def_route = vec!["route", "add"]; + def_route.extend(to); + run_command("ip", &def_route)?; + Ok(()) +} + +/// Updates the settings default route, returns true if an edit to the settings has been performed +pub fn update_settings_route( + settings_default_route: &mut Option, +) -> Result { + let def_route = match get_default_route()? { + Some(route) => route, + None => return Ok(false), + }; + if !def_route.is_althea_default_route() { + // update the default route if default route is not wg exit + *settings_default_route = Some(def_route); + Ok(true) + } else { + Ok(false) } +} - /// sets the manual route for a peer using ip route, returns true if the settings - /// have been updated - pub fn manual_peers_route( - &self, - endpoint_ip: &IpAddr, - settings_default_route: &mut Option, - ) -> Result { - let changed = self.update_settings_route(settings_default_route)?; - match settings_default_route { - Some(d) => { - self.set_route(&IpRoute::ToSubnet(ToSubnet { - dst: *endpoint_ip, - subnet: 32, - via: Some(d.via), - nic: d.nic.to_string(), - proto: Some("static".to_string()), - metric: None, - src: None, - scope: None, - }))?; - Ok(changed) - } - // no default route, nothing to do - None => Ok(changed), +/// sets the manual route for a peer using ip route, returns true if the settings +/// have been updated +pub fn manual_peers_route( + endpoint_ip: &IpAddr, + settings_default_route: &mut Option, +) -> Result { + let changed = update_settings_route(settings_default_route)?; + match settings_default_route { + Some(d) => { + set_route(&IpRoute::ToSubnet(ToSubnet { + dst: *endpoint_ip, + subnet: 32, + via: Some(d.via), + nic: d.nic.to_string(), + proto: Some("static".to_string()), + metric: None, + src: None, + scope: None, + }))?; + Ok(changed) } + // no default route, nothing to do + None => Ok(changed), } +} - /// restore the default route, if we find a default route is already in place that is not - /// our wg_exit route we grab that one, save it off, and make no changes. - pub fn restore_default_route( - &self, - settings_default_route: &mut Option, - ) -> Result<(), Error> { - let current_route = self.get_default_route()?; - match current_route.clone() { - Some(d) => { - if d.is_althea_default_route() { - // if we didn't have a default route already it's restored by default - if let Some(route) = settings_default_route { - self.set_route(&IpRoute::DefaultRoute(route.clone()))?; - } - } else { - *settings_default_route = Some(current_route.unwrap()); - } - } - None => { +/// restore the default route, if we find a default route is already in place that is not +/// our wg_exit route we grab that one, save it off, and make no changes. +pub fn restore_default_route( + settings_default_route: &mut Option, +) -> Result<(), Error> { + let current_route = get_default_route()?; + match current_route.clone() { + Some(d) => { + if d.is_althea_default_route() { // if we didn't have a default route already it's restored by default if let Some(route) = settings_default_route { - self.set_route(&IpRoute::DefaultRoute(route.clone()))?; + set_route(&IpRoute::DefaultRoute(route.clone()))?; } + } else { + *settings_default_route = Some(current_route.unwrap()); } - }; - Ok(()) - } + } + None => { + // if we didn't have a default route already it's restored by default + if let Some(route) = settings_default_route { + set_route(&IpRoute::DefaultRoute(route.clone()))?; + } + } + }; + Ok(()) } #[test] //"default", "via", "64.146.145.5", "dev", "eth0", "proto", "static", "linkdown" fn test_get_default_route_invalid() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - assert_eq!(program, "ip"); - assert_eq!(args, vec!["route", "list", "default"]); - Ok(Output { - stdout: b"1.2.3.4/16 dev interface scope link metric 1000".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - })); - - assert!( - KI.get_default_route().unwrap().is_none(), - "Invalid `ip route` unexpectedly returned a valid route" - ); + let route = "1.2.3.4/16 dev interface scope link metric 1000"; + let res = get_default_route_internal(route.to_string()); + assert!(res.unwrap().is_none()) } #[test] @@ -346,41 +323,16 @@ fn test_parse_routes() { #[test] fn test_get_default_route() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - let mut counter = 0; - - // This will mock `run_command` to run a real output of `ip route` - // with addition that there are additional spaces, more than one default - // route etc. - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - assert_eq!(program, "ip"); - assert_eq!(args, vec!["route", "list", "default"]); - Ok(Output { - stdout: b" + let stdout = " 169.254.0.0/16 dev wifiinterface scope link metric 1000 172.16.82.0/24 dev vmnet1 proto kernel scope link src 172.16.82.1 default via 192.168.8.1 dev wifiinterface proto dhcp metric 600 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 192.168.8.0/24 dev wifiinterface proto kernel scope link src 192.168.8.175 metric 600 default via 192.168.9.1 dev wifiinterface proto dhcp metric 1200 -192.168.36.0/24 dev vmnet8 proto kernel scope link src 192.168.36.1" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - })); +192.168.36.0/24 dev vmnet8 proto kernel scope link src 192.168.36.1"; - let result = KI - .get_default_route() + let result = get_default_route_internal(stdout.to_string()) .expect("Unable to get default route") .unwrap(); let correct = DefaultRoute { @@ -395,111 +347,6 @@ default via 192.168.9.1 dev wifiinterface proto dhcp metric 1200 #[test] fn test_set_route() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - assert_eq!(program, "ip"); - assert_eq!( - args, - vec![ - "route", - "add", - "127.0.0.1", - "via", - "127.0.0.2", - "dev", - "eno3p", - "proto", - "static" - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - assert_eq!(program, "ip"); - assert_eq!( - args, - vec![ - "route", - "add", - "127.0.0.1", - "via", - "127.0.0.2", - "dev", - "eno3p", - "proto", - "static" - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 3 => { - assert_eq!(program, "ip"); - assert_eq!( - args, - vec![ - "route", - "add", - "127.0.0.1/24", - "dev", - "eno3p", - "proto", - "static" - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 4 => { - assert_eq!(program, "ip"); - assert_eq!( - args, - vec![ - "route", - "add", - "127.0.0.1/24", - "via", - "127.0.0.2", - "dev", - "eno3p", - "proto", - "static", - "src", - "127.0.0.2" - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - })); - // as it would be normally parsed let route = IpRoute::ToSubnet(ToSubnet { dst: "127.0.0.1".parse().unwrap(), @@ -511,9 +358,10 @@ fn test_set_route() { metric: None, scope: None, }); - // without proto we insert proto static to prevent other - // programs from messing with our routes so it should still be there - KI.set_route(&route).expect("Unable to set route"); + assert_eq!( + route.to_string(), + "127.0.0.1 via 127.0.0.2 dev eno3p proto static " + ); let route = IpRoute::ToSubnet(ToSubnet { dst: "127.0.0.1".parse().unwrap(), subnet: 32, @@ -524,7 +372,10 @@ fn test_set_route() { metric: None, scope: None, }); - KI.set_route(&route).expect("Unable to set route"); + assert_eq!( + route.to_string(), + "127.0.0.1 via 127.0.0.2 dev eno3p proto static " + ); // without via, with subnet let route = IpRoute::ToSubnet(ToSubnet { dst: "127.0.0.1".parse().unwrap(), @@ -536,7 +387,7 @@ fn test_set_route() { metric: None, scope: None, }); - KI.set_route(&route).expect("Unable to set route"); + assert_eq!(route.to_string(), "127.0.0.1/24 dev eno3p proto static "); // without via, with subnet with other options let route = IpRoute::ToSubnet(ToSubnet { dst: "127.0.0.1".parse().unwrap(), @@ -550,71 +401,14 @@ fn test_set_route() { // will be ignored scope: Some("link".to_string()), }); - KI.set_route(&route).expect("Unable to set route"); + assert_eq!( + route.to_string(), + "127.0.0.1/24 via 127.0.0.2 dev eno3p proto static src 127.0.0.2 " + ); } #[test] fn test_set_default_route() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - assert_eq!(program, "ip"); - assert_eq!( - args, - vec![ - "route", - "add", - "default", - "via", - "192.168.8.1", - "dev", - "wifiinterface", - "proto", - "static" - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - assert_eq!(program, "ip"); - assert_eq!( - args, - vec![ - "route", - "add", - "default", - "via", - "192.168.8.1", - "dev", - "wifiinterface", - "proto", - "static", - "src", - "127.0.0.2" - ] - ); - - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - })); let correct = IpRoute::DefaultRoute(DefaultRoute { via: "192.168.8.1".parse().unwrap(), nic: "wifiinterface".to_string(), @@ -622,8 +416,10 @@ fn test_set_default_route() { metric: Some(600), src: None, }); - - KI.set_route(&correct).expect("Unable to set default route"); + assert_eq!( + correct.to_string(), + "default via 192.168.8.1 dev wifiinterface proto static" + ); let correct = IpRoute::DefaultRoute(DefaultRoute { via: "192.168.8.1".parse().unwrap(), @@ -632,6 +428,8 @@ fn test_set_default_route() { metric: Some(600), src: Some("127.0.0.2".parse().unwrap()), }); - - KI.set_route(&correct).expect("Unable to set default route"); + assert_eq!( + correct.to_string(), + "default via 192.168.8.1 dev wifiinterface proto static src 127.0.0.2" + ); } diff --git a/althea_kernel_interface/src/iptables.rs b/althea_kernel_interface/src/iptables.rs index 80e284733..6b849bd66 100644 --- a/althea_kernel_interface/src/iptables.rs +++ b/althea_kernel_interface/src/iptables.rs @@ -1,54 +1,36 @@ -use crate::KernelInterface; -use crate::KernelInterfaceError; +use crate::{run_command, KernelInterfaceError}; -impl dyn KernelInterface { - /// -I and -A will be checked to see if they already exist before adding. - pub fn add_iptables_rule( - &self, - command: &str, - rule: &[&str], - ) -> Result<(), KernelInterfaceError> { - assert!(rule.contains(&"-A") || rule.contains(&"-I") || rule.contains(&"-D")); +/// -I and -A will be checked to see if they already exist before adding. +pub fn add_iptables_rule(command: &str, rule: &[&str]) -> Result<(), KernelInterfaceError> { + assert!(rule.contains(&"-A") || rule.contains(&"-I") || rule.contains(&"-D")); - // we replace the add or append commands with a check command so that we can see if the rule is actually present - // if it is then we don't need to do anything - let mut new_command = Vec::new(); - let mut i_pos_skip = None; + // we replace the add or append commands with a check command so that we can see if the rule is actually present + // if it is then we don't need to do anything + let mut new_command = Vec::new(); + let mut i_pos_skip = None; - for (i, rul) in rule.iter().enumerate() { - if i_pos_skip.is_some() && i == i_pos_skip.unwrap() { - continue; - } - if *rul == "-I" { - // if there's an insertion index, then skip it for the check statement - if rule[i + 2].parse::().is_ok() { - i_pos_skip = Some(i + 2); - } - new_command.push("-C"); - } else if *rul == "-A" { - new_command.push("-C"); - } else { - new_command.push(rul); - } + for (i, rul) in rule.iter().enumerate() { + if i_pos_skip.is_some() && i == i_pos_skip.unwrap() { + continue; } - - let check = self.run_command(command, &new_command)?; - - if !check.status.success() { - self.run_command(command, rule)?; + if *rul == "-I" { + // if there's an insertion index, then skip it for the check statement + if rule[i + 2].parse::().is_ok() { + i_pos_skip = Some(i + 2); + } + new_command.push("-C"); + } else if *rul == "-A" { + new_command.push("-C"); + } else { + new_command.push(rul); } - - Ok(()) } - pub fn check_iptable_rule( - &self, - command: &str, - rule: &[&str], - ) -> Result { - assert!(rule.contains(&"-C")); + let check = run_command(command, &new_command)?; - let check = self.run_command(command, rule)?; - Ok(check.status.success()) + if !check.status.success() { + run_command(command, rule)?; } + + Ok(()) } diff --git a/althea_kernel_interface/src/is_openwrt.rs b/althea_kernel_interface/src/is_openwrt.rs index ac6c6791c..29cf45d87 100644 --- a/althea_kernel_interface/src/is_openwrt.rs +++ b/althea_kernel_interface/src/is_openwrt.rs @@ -1,15 +1,12 @@ -use super::KernelInterface; use std::process::{Command, Stdio}; -impl dyn KernelInterface { - /// Checks if the local system is openwrt - pub fn is_openwrt(&self) -> bool { - let uname = Command::new("cat") - .args(["/etc/openwrt_release"]) - .stdout(Stdio::piped()) - .output() - .unwrap(); - let uname_results = String::from_utf8(uname.stdout).unwrap(); - uname_results.contains("OpenWrt") - } +/// Checks if the local system is openwrt +pub fn is_openwrt() -> bool { + let uname = Command::new("cat") + .args(["/etc/openwrt_release"]) + .stdout(Stdio::piped()) + .output() + .unwrap(); + let uname_results = String::from_utf8(uname.stdout).unwrap(); + uname_results.contains("OpenWrt") } diff --git a/althea_kernel_interface/src/lib.rs b/althea_kernel_interface/src/lib.rs index a292e9ab9..28b9c946f 100644 --- a/althea_kernel_interface/src/lib.rs +++ b/althea_kernel_interface/src/lib.rs @@ -5,6 +5,8 @@ extern crate log; #[macro_use] extern crate serde_derive; +use std::str; +use std::str::Utf8Error; use std::{env, fmt}; use std::{ error::Error, @@ -15,45 +17,38 @@ use std::{ num::ParseFloatError, process::{Command, Output}, }; -use std::{ - str::Utf8Error, - sync::{Arc, Mutex}, -}; - -use std::str; -mod babel; +pub mod babel; pub mod bridge_tools; -mod check_cron; -mod counter; -mod create_wg_key; -mod delete_tunnel; -mod dns; +pub mod check_cron; +pub mod counter; +pub mod create_wg_key; +pub mod dns; pub mod exit_client_tunnel; -mod exit_server_tunnel; +pub mod exit_server_tunnel; pub mod file_io; -mod fs_sync; +pub mod fs_sync; mod get_neighbors; pub mod hardware_info; -mod interface_tools; -mod ip_addr; +pub mod interface_tools; +pub mod ip_addr; pub mod ip_neigh; -mod ip_route; +pub mod ip_route; mod iptables; -mod is_openwrt; -mod link_local_tools; -mod manipulate_uci; +pub mod is_openwrt; +pub mod link_local_tools; +pub mod manipulate_uci; mod netfilter; pub mod netns; pub mod open_tunnel; -mod openwrt_ubus; +pub mod openwrt_ubus; pub mod opkg_feeds; -mod ping_check; -mod set_system_password; -mod setup_wg_if; +pub mod ping_check; +pub mod set_system_password; +pub mod setup_wg_if; pub mod time; -mod traffic_control; -mod udp_socket_table; +pub mod traffic_control; +pub mod udp_socket_table; pub mod upgrade; pub mod wg_iface_counter; @@ -72,9 +67,6 @@ use std::io::Error as IoError; use std::net::AddrParseError; use std::string::FromUtf8Error; -type CommandFunction = - Box) -> Result + Send>; - #[derive(Clone, Debug)] pub enum KernelInterfaceError { RuntimeError(String), @@ -194,25 +186,6 @@ impl From for KernelInterfaceError { } } -#[cfg(test)] -lazy_static! { - pub static ref KI: Box = Box::new(TestCommandRunner { - run_command: Arc::new(Mutex::new(Box::new(|_program, _args| { - panic!("kernel interface used before initialized"); - }))) - }); -} - -#[cfg(not(test))] -lazy_static! { - pub static ref KI: Box = Box::new(LinuxCommandRunner {}); -} - -pub trait CommandRunner { - fn run_command(&self, program: &str, args: &[&str]) -> Result; - fn set_mock(&self, mock: CommandFunction); -} - // a quick throwaway function to print arguments arrays so that they can be copy/pasted from logs fn print_str_array(input: &[&str]) -> String { let mut output = String::new(); @@ -222,86 +195,51 @@ fn print_str_array(input: &[&str]) -> String { output } -pub struct LinuxCommandRunner; - -impl CommandRunner for LinuxCommandRunner { - fn run_command(&self, program: &str, args: &[&str]) -> Result { - let start = Instant::now(); - let output = match Command::new(program).args(args).output() { - Ok(o) => o, - Err(e) => { - if e.kind() == ErrorKind::NotFound { - error!("The {:?} binary was not found. Please install a package that provides it. PATH={:?}", program, env::var("PATH")); - } - return Err(e.into()); +pub fn run_command(program: &str, args: &[&str]) -> Result { + let start = Instant::now(); + let output = match Command::new(program).args(args).output() { + Ok(o) => o, + Err(e) => { + if e.kind() == ErrorKind::NotFound { + error!("The {:?} binary was not found. Please install a package that provides it. PATH={:?}", program, env::var("PATH")); } - }; - + return Err(e.into()); + } + }; + + trace!( + "Command {} {} returned: {:?}", + program, + print_str_array(args), + output + ); + if !output.status.success() { trace!( - "Command {} {} returned: {:?}", + "Command {} {} returned: an error {:?}", program, print_str_array(args), output ); - if !output.status.success() { - trace!( - "Command {} {} returned: an error {:?}", - program, - print_str_array(args), - output - ); - } - trace!( - "command completed in {}s {}ms", - start.elapsed().as_secs(), - start.elapsed().subsec_millis() - ); - - if start.elapsed().as_secs() > 5 { - error!( - "Command {} {} took more than five seconds to complete!", - program, - print_str_array(args) - ); - } else if start.elapsed().as_secs() > 1 { - warn!( - "Command {} {} took more than one second to complete!", - program, - print_str_array(args) - ); - } - - Ok(output) - } - - fn set_mock( - &self, - _mock: Box) -> Result + Send>, - ) { - unimplemented!() } -} - -pub struct TestCommandRunner { - pub run_command: Arc>, -} - -impl CommandRunner for TestCommandRunner { - fn run_command(&self, program: &str, args: &[&str]) -> Result { - let mut args_owned = Vec::new(); - for a in args { - args_owned.push((*a).to_string()) - } - - (*self.run_command.lock().unwrap())(program.to_string(), args_owned) + trace!( + "command completed in {}s {}ms", + start.elapsed().as_secs(), + start.elapsed().subsec_millis() + ); + + if start.elapsed().as_secs() > 5 { + error!( + "Command {} {} took more than five seconds to complete!", + program, + print_str_array(args) + ); + } else if start.elapsed().as_secs() > 1 { + warn!( + "Command {} {} took more than one second to complete!", + program, + print_str_array(args) + ); } - fn set_mock(&self, mock: CommandFunction) { - *self.run_command.lock().unwrap() = mock - } + Ok(output) } - -pub trait KernelInterface: CommandRunner + Sync + Send {} - -impl KernelInterface for LinuxCommandRunner {} -impl KernelInterface for TestCommandRunner {} diff --git a/althea_kernel_interface/src/link_local_tools.rs b/althea_kernel_interface/src/link_local_tools.rs index 75113dbcf..abf941704 100644 --- a/althea_kernel_interface/src/link_local_tools.rs +++ b/althea_kernel_interface/src/link_local_tools.rs @@ -1,4 +1,4 @@ -use crate::{file_io::get_lines, KernelInterface, KernelInterfaceError}; +use crate::{file_io::get_lines, get_neighbors::get_neighbors, run_command, KernelInterfaceError}; use regex::Regex; use std::{ fs::read_dir, @@ -57,174 +57,138 @@ fn get_link_local_device_ip_internal( )) } -impl dyn KernelInterface { - fn get_proc_net_path(&self) -> String { - if cfg!(feature = "integration_test") { - // this is extremely overcomplicated and needs to be replaced by netlink at some point - // essentially we find the namespace file of a process spanwed in the namespace (babel) - // and then read it, only works in integration tests. +fn get_proc_net_path() -> String { + if cfg!(feature = "integration_test") { + // this is extremely overcomplicated and needs to be replaced by netlink at some point + // essentially we find the namespace file of a process spanwed in the namespace (babel) + // and then read it, only works in integration tests. - let ns = self.run_command("ip", &["netns", "identify"]).unwrap(); - let ns = match String::from_utf8(ns.stdout) { - Ok(s) => s, - Err(_) => panic!("Could not get netns name!"), - }; - let ns = ns.trim(); - - let links = read_dir("/proc/").unwrap(); - - // in the legacy test namespaces are netlab-1 but interfaces are veth-1-2 - // so this lets us do the conversion - let legacy_test_ns_number = ns.strip_prefix("netlab-"); - - for dir in links { - let dir = dir.unwrap(); - if dir.path().is_dir() { - let dir_name = dir.file_name().to_str().unwrap().to_string(); - // we're looking for a pid - if let Ok(number) = dir_name.trim().parse() { - let number: u32 = number; - let path = format!("/proc/{number}/net/if_inet6"); - if let Ok(lines) = get_lines(&path) { - for line in lines { - let line = line.split_ascii_whitespace().last().unwrap(); - let prefix = - if let Some(legacy_test_ns_number) = legacy_test_ns_number { - format!("veth-{legacy_test_ns_number}") - } else { - format!("veth-{ns}") - }; - if line.starts_with(&prefix) { - return path; - } + let ns = run_command("ip", &["netns", "identify"]).unwrap(); + let ns = match String::from_utf8(ns.stdout) { + Ok(s) => s, + Err(_) => panic!("Could not get netns name!"), + }; + let ns = ns.trim(); + + let links = read_dir("/proc/").unwrap(); + + // in the legacy test namespaces are netlab-1 but interfaces are veth-1-2 + // so this lets us do the conversion + let legacy_test_ns_number = ns.strip_prefix("netlab-"); + + for dir in links { + let dir = dir.unwrap(); + if dir.path().is_dir() { + let dir_name = dir.file_name().to_str().unwrap().to_string(); + // we're looking for a pid + if let Ok(number) = dir_name.trim().parse() { + let number: u32 = number; + let path = format!("/proc/{number}/net/if_inet6"); + if let Ok(lines) = get_lines(&path) { + for line in lines { + let line = line.split_ascii_whitespace().last().unwrap(); + let prefix = if let Some(legacy_test_ns_number) = legacy_test_ns_number + { + format!("veth-{legacy_test_ns_number}") + } else { + format!("veth-{ns}") + }; + if line.starts_with(&prefix) { + return path; } } } } } - panic!( - "We did not find the babel process to locate this rita threads namespace {}!", - ns - ); - } else { - // standard location - "/proc/net/if_inet6".to_string() } + panic!( + "We did not find the babel process to locate this rita threads namespace {}!", + ns + ); + } else { + // standard location + "/proc/net/if_inet6".to_string() } +} - /// This gets our link local ip for a given device - pub fn get_link_local_device_ip(&self, dev: &str) -> Result { - let path = self.get_proc_net_path(); - let lines = get_lines(&path)?; - get_link_local_device_ip_internal(lines, dev, true) - } - - /// This gets our global ip for a given device - pub fn get_global_device_ip(&self, dev: &str) -> Result { - let path = self.get_proc_net_path(); - let lines = get_lines(&path)?; - get_link_local_device_ip_internal(lines, dev, false) - } +/// This gets our link local ip for a given device +pub fn get_link_local_device_ip(dev: &str) -> Result { + let path = get_proc_net_path(); + let lines = get_lines(&path)?; + get_link_local_device_ip_internal(lines, dev, true) +} - pub fn get_global_device_ip_v4(&self, dev: &str) -> Result { - let output = self.run_command("ip", &["addr", "show", "dev", dev, "scope", "global"])?; - trace!("Got {:?} from `ip addr`", output); +/// This gets our global ip for a given device +pub fn get_global_device_ip(dev: &str) -> Result { + let path = get_proc_net_path(); + let lines = get_lines(&path)?; + get_link_local_device_ip_internal(lines, dev, false) +} - lazy_static! { - static ref RE: Regex = Regex::new(r"inet (\S*?)(/[0-9]+)? scope global") - .expect("Unable to compile regular expression"); - } +pub fn get_global_device_ip_v4(dev: &str) -> Result { + let output = run_command("ip", &["addr", "show", "dev", dev, "scope", "global"])?; + trace!("Got {:?} from `ip addr`", output); - let cap_str = String::from_utf8(output.stdout)?; - let cap = RE.captures(&cap_str); - if let Some(cap) = cap { - trace!("got global IP of {} from device {}", &cap[1], &dev); - Ok(cap[1].parse::()?) - } else { - Err(KernelInterfaceError::RuntimeError( - "No global found or no interface found".to_string(), - )) - } + lazy_static! { + static ref RE: Regex = Regex::new(r"inet (\S*?)(/[0-9]+)? scope global") + .expect("Unable to compile regular expression"); } - /// Given a neighboring link local ip, return the device name - pub fn get_device_name(&self, their_ip: IpAddr) -> Result { - let neigh = self.get_neighbors()?; - trace!("looking for {:?} in {:?} for device name", their_ip, neigh); - for (ip, dev) in neigh { - if ip == their_ip { - return Ok(dev); - } - } - + let cap_str = String::from_utf8(output.stdout)?; + let cap = RE.captures(&cap_str); + if let Some(cap) = cap { + trace!("got global IP of {} from device {}", &cap[1], &dev); + Ok(cap[1].parse::()?) + } else { Err(KernelInterfaceError::RuntimeError( - "Address not found in neighbors".to_string(), + "No global found or no interface found".to_string(), )) } +} - /// This gets our link local ip that can be reached by another node with link local ip - pub fn get_reply_ip( - &self, - their_ip: Ipv6Addr, - external_interface: Option, - ) -> Result { - let neigh = self.get_neighbors()?; - - trace!("Looking for {:?} in {:?} for reply ip", their_ip, neigh); - for (ip, dev) in neigh { - if ip == their_ip { - return self.get_link_local_device_ip(&dev); - } - } - - if let Some(external_interface) = external_interface { - let global_ip = self.get_global_device_ip(&external_interface)?; - trace!( - "Didn't find {:?} in neighbors, sending global ip {:?}", - their_ip, - global_ip - ); - Ok(global_ip) - } else { - trace!("Didn't find {:?} in neighbors, bailing out", their_ip); - Err(KernelInterfaceError::RuntimeError( - "Address not found in neighbors".to_string(), - )) +/// Given a neighboring link local ip, return the device name +pub fn get_device_name(their_ip: IpAddr) -> Result { + let neigh = get_neighbors()?; + trace!("looking for {:?} in {:?} for device name", their_ip, neigh); + for (ip, dev) in neigh { + if ip == their_ip { + return Ok(dev); } } -} -#[test] -fn test_get_device_name_linux() { - use crate::KI; - - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "ip"); - assert_eq!(args, &["neigh"]); + Err(KernelInterfaceError::RuntimeError( + "Address not found in neighbors".to_string(), + )) +} - Ok(Output { - stdout: b"10.0.2.2 dev eth0 lladdr 00:00:00:aa:00:03 STALE -10.0.0.2 dev eth0 FAILED -10.0.1.2 dev eth0 lladdr 00:00:00:aa:00:05 REACHABLE -2001::2 dev eth0 lladdr 00:00:00:aa:00:56 REACHABLE -fe80::7459:8eff:fe98:81 dev eth0 lladdr 76:59:8e:98:00:81 STALE -fe80::433:25ff:fe8c:e1ea dev eth2 lladdr 1a:32:06:78:05:0a STALE -2001::2 dev eth0 FAILED" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - })); +/// This gets our link local ip that can be reached by another node with link local ip +pub fn get_reply_ip( + their_ip: Ipv6Addr, + external_interface: Option, +) -> Result { + let neigh = get_neighbors()?; - let dev = KI - .get_device_name("fe80::433:25ff:fe8c:e1ea".parse().unwrap()) - .unwrap(); + trace!("Looking for {:?} in {:?} for reply ip", their_ip, neigh); + for (ip, dev) in neigh { + if ip == their_ip { + return get_link_local_device_ip(&dev); + } + } - assert_eq!(dev, "eth2") + if let Some(external_interface) = external_interface { + let global_ip = get_global_device_ip(&external_interface)?; + trace!( + "Didn't find {:?} in neighbors, sending global ip {:?}", + their_ip, + global_ip + ); + Ok(global_ip) + } else { + trace!("Didn't find {:?} in neighbors, bailing out", their_ip); + Err(KernelInterfaceError::RuntimeError( + "Address not found in neighbors".to_string(), + )) + } } #[test] diff --git a/althea_kernel_interface/src/manipulate_uci.rs b/althea_kernel_interface/src/manipulate_uci.rs index d7cda525a..2db140d77 100644 --- a/althea_kernel_interface/src/manipulate_uci.rs +++ b/althea_kernel_interface/src/manipulate_uci.rs @@ -1,156 +1,151 @@ -use crate::{KernelInterface, KernelInterfaceError}; +use crate::{run_command, KernelInterfaceError}; use regex::Regex; use std::collections::HashMap; -impl dyn KernelInterface { - fn run_uci(&self, command: &str, args: &[&str]) -> Result<(), KernelInterfaceError> { - let output = self.run_command(command, args)?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error while setting UCI: {}", - String::from_utf8(output.stderr)? - ))); - } - Ok(()) +fn run_uci(command: &str, args: &[&str]) -> Result<(), KernelInterfaceError> { + let output = run_command(command, args)?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error while setting UCI: {}", + String::from_utf8(output.stderr)? + ))); } + Ok(()) +} + +/// Sets an arbitrary UCI variable on OpenWRT +pub fn set_uci_var(key: &str, value: &str) -> Result<(), KernelInterfaceError> { + run_uci("uci", &["set", &format!("{key}={value}")])?; + Ok(()) +} - /// Sets an arbitrary UCI variable on OpenWRT - pub fn set_uci_var(&self, key: &str, value: &str) -> Result<(), KernelInterfaceError> { - self.run_uci("uci", &["set", &format!("{key}={value}")])?; - Ok(()) +/// Adds an arbitrary UCI variable on OpenWRT +pub fn add_uci_var(key: &str, value: &str) -> Result<(), KernelInterfaceError> { + run_uci("uci", &["add", key, value])?; + Ok(()) +} + +/// Sets an arbitrary UCI list on OpenWRT +pub fn set_uci_list(key: &str, value: &[&str]) -> Result<(), KernelInterfaceError> { + if let Err(e) = del_uci_var(key) { + trace!("Delete uci var failed! {:?}", e); } - /// Adds an arbitrary UCI variable on OpenWRT - pub fn add_uci_var(&self, key: &str, value: &str) -> Result<(), KernelInterfaceError> { - self.run_uci("uci", &["add", key, value])?; - Ok(()) + for v in value { + run_uci("uci", &["add_list", &format!("{}={}", &key, &v)])?; } + Ok(()) +} - /// Sets an arbitrary UCI list on OpenWRT - pub fn set_uci_list(&self, key: &str, value: &[&str]) -> Result<(), KernelInterfaceError> { - if let Err(e) = self.del_uci_var(key) { - trace!("Delete uci var failed! {:?}", e); - } +/// Deletes an arbitrary UCI variable on OpenWRT +pub fn del_uci_var(key: &str) -> Result<(), KernelInterfaceError> { + run_uci("uci", &["delete", key])?; + Ok(()) +} - for v in value { - self.run_uci("uci", &["add_list", &format!("{}={}", &key, &v)])?; - } - Ok(()) +/// Retrieves the value of a given UCI path, could be one or multiple values +pub fn get_uci_var(key: &str) -> Result { + let output = run_command("uci", &["get", key])?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error while getting UCI: {}", + String::from_utf8(output.stderr)? + ))); } + let clean_string = String::from_utf8(output.stdout)?.trim().to_string(); + Ok(clean_string) +} - /// Deletes an arbitrary UCI variable on OpenWRT - pub fn del_uci_var(&self, key: &str) -> Result<(), KernelInterfaceError> { - self.run_uci("uci", &["delete", key])?; - Ok(()) +/// Commits changes to UCI, returns true if successful +pub fn uci_commit(subsection: &str) -> Result<(), KernelInterfaceError> { + let output = run_command("uci", &["commit", subsection])?; + if !output.status.success() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error while commiting UCI: {}", + String::from_utf8(output.stderr)? + ))); } + Ok(()) +} - /// Retrieves the value of a given UCI path, could be one or multiple values - pub fn get_uci_var(&self, key: &str) -> Result { - let output = self.run_command("uci", &["get", key])?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error while getting UCI: {}", - String::from_utf8(output.stderr)? - ))); - } - let clean_string = String::from_utf8(output.stdout)?.trim().to_string(); - Ok(clean_string) +/// Resets unsaved changes to UCI +pub fn uci_revert(section: &str) -> Result<(), KernelInterfaceError> { + let output = run_command("uci", &["revert", section])?; + if !output.status.success() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error while reverting UCI: {}", + String::from_utf8(output.stderr)? + ))); } + Ok(()) +} - /// Commits changes to UCI, returns true if successful - pub fn uci_commit(&self, subsection: &str) -> Result<(), KernelInterfaceError> { - let output = self.run_command("uci", &["commit", subsection])?; - if !output.status.success() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error while commiting UCI: {}", - String::from_utf8(output.stderr)? - ))); - } - Ok(()) +pub fn refresh_initd(program: &str) -> Result<(), KernelInterfaceError> { + let output = run_command(&format!("/etc/init.d/{program}"), &["reload"])?; + if !output.status.success() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error while refreshing {}: {}", + program, + String::from_utf8(output.stderr)? + ))); } + Ok(()) +} - /// Resets unsaved changes to UCI - pub fn uci_revert(&self, section: &str) -> Result<(), KernelInterfaceError> { - let output = self.run_command("uci", &["revert", section])?; - if !output.status.success() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error while reverting UCI: {}", - String::from_utf8(output.stderr)? - ))); - } - Ok(()) +/// Obtain a HashMap of a UCI section's entries and their values. Whenever `section` is None +/// the function will shell out for just `uci show` which fetches all available config entries. +pub fn uci_show(section: Option<&str>) -> Result, KernelInterfaceError> { + lazy_static! { + static ref RE: Regex = Regex::new(r"(.+)=(.+)").unwrap(); } - pub fn refresh_initd(&self, program: &str) -> Result<(), KernelInterfaceError> { - let output = self.run_command(&format!("/etc/init.d/{program}"), &["reload"])?; - if !output.status.success() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error while refreshing {}: {}", - program, - String::from_utf8(output.stderr)? - ))); - } - Ok(()) + let output = match section { + Some(s) => run_command("uci", &["show", s])?, + None => run_command("uci", &["show"])?, + }; + + if !output.status.success() { + return Err(KernelInterfaceError::RuntimeError(format!( + "`uci show` experienced a problem:\nstdout:\n{:?}\nstderr:\n{:?}", + String::from_utf8(output.stdout)?, + String::from_utf8(output.stderr)? + ))); } - /// Obtain a HashMap of a UCI section's entries and their values. Whenever `section` is None - /// the function will shell out for just `uci show` which fetches all available config entries. - pub fn uci_show( - &self, - section: Option<&str>, - ) -> Result, KernelInterfaceError> { - lazy_static! { - static ref RE: Regex = Regex::new(r"(.+)=(.+)").unwrap(); - } - - let output = match section { - Some(s) => self.run_command("uci", &["show", s])?, - None => self.run_command("uci", &["show"])?, - }; + let stdout = String::from_utf8(output.stdout)?; - if !output.status.success() { - return Err(KernelInterfaceError::RuntimeError(format!( - "`uci show` experienced a problem:\nstdout:\n{:?}\nstderr:\n{:?}", - String::from_utf8(output.stdout)?, - String::from_utf8(output.stderr)? - ))); - } - - let stdout = String::from_utf8(output.stdout)?; - - let mut retval: HashMap = HashMap::new(); - - for line in stdout.lines() { - let caps = match RE.captures(line) { - Some(c) => c, - None => { - return Err(KernelInterfaceError::RuntimeError(format!( - "uci_show: Could not match regex {:?} on line {:?}", - *RE, line - ))) - } - }; - retval.insert( - caps[1].to_owned(), - caps[2].to_owned().trim_matches('\'').to_string(), - ); - } - - Ok(retval) - } + let mut retval: HashMap = HashMap::new(); - pub fn openwrt_reset_wireless(&self) -> Result<(), KernelInterfaceError> { - self.run_command("wifi", &[])?; - Ok(()) + for line in stdout.lines() { + let caps = match RE.captures(line) { + Some(c) => c, + None => { + return Err(KernelInterfaceError::RuntimeError(format!( + "uci_show: Could not match regex {:?} on line {:?}", + *RE, line + ))) + } + }; + retval.insert( + caps[1].to_owned(), + caps[2].to_owned().trim_matches('\'').to_string(), + ); } - pub fn openwrt_reset_network(&self) -> Result<(), KernelInterfaceError> { - self.run_command("/etc/init.d/network", &["restart"])?; - Ok(()) - } + Ok(retval) +} - pub fn openwrt_reset_dnsmasq(&self) -> Result<(), KernelInterfaceError> { - self.run_command("/etc/init.d/dnsmasq", &["restart"])?; - Ok(()) - } +pub fn openwrt_reset_wireless() -> Result<(), KernelInterfaceError> { + run_command("wifi", &[])?; + Ok(()) +} + +pub fn openwrt_reset_network() -> Result<(), KernelInterfaceError> { + run_command("/etc/init.d/network", &["restart"])?; + Ok(()) +} + +pub fn openwrt_reset_dnsmasq() -> Result<(), KernelInterfaceError> { + run_command("/etc/init.d/dnsmasq", &["restart"])?; + Ok(()) } diff --git a/althea_kernel_interface/src/netfilter.rs b/althea_kernel_interface/src/netfilter.rs index ef8f15bb1..2d1be0732 100644 --- a/althea_kernel_interface/src/netfilter.rs +++ b/althea_kernel_interface/src/netfilter.rs @@ -1,235 +1,255 @@ +use crate::{run_command, KernelInterfaceError}; use std::net::IpAddr; -use crate::KernelInterface; -use crate::KernelInterfaceError; +pub fn does_nftables_exist() -> bool { + let output = match run_command("nft", &["-v"]) { + Ok(out) => out, + Err(e) => { + error!("Run command is failing with {}", e); + // Assume there is no nftables + return false; + } + }; -impl dyn KernelInterface { - pub fn does_nftables_exist(&self) -> bool { - let output = match self.run_command("nft", &["-v"]) { - Ok(out) => out, - Err(e) => { - error!("Run command is failing with {}", e); - // Assume there is no nftables - return false; - } - }; + let stdout = match String::from_utf8(output.stdout) { + Ok(a) => a, + Err(e) => { + error!("Cannot parse stdout with {}", e); + return false; + } + }; - let stdout = match String::from_utf8(output.stdout) { - Ok(a) => a, - Err(e) => { - error!("Cannot parse stdout with {}", e); - return false; - } - }; + stdout.contains("nftables") +} - stdout.contains("nftables") - } +fn create_fwd_rule() -> Result<(), KernelInterfaceError> { + run_command( + "nft", + &["insert", "rule", "inet", "fw4", "forward_lan", "accept"], + )?; - fn create_fwd_rule(&self) -> Result<(), KernelInterfaceError> { - self.run_command( - "nft", - &["insert", "rule", "inet", "fw4", "forward_lan", "accept"], - )?; + Ok(()) +} - Ok(()) +fn create_nft_set(set_name: &str) { + if let Err(e) = run_command( + "nft", + &[ + "add", + "set", + "inet", + "fw4", + set_name, + "{", + "type", + "ipv6_addr", + ".", + "ifname;", + "flags", + "dynamic;", + "counter;", + "size", + "65535;", + "}", + ], + ) { + error!("Unable to setup counter tables with {:?}", e); } +} - fn create_nft_set(&self, set_name: &str) { - if let Err(e) = self.run_command( - "nft", - &[ - "add", - "set", - "inet", - "fw4", - set_name, - "{", - "type", - "ipv6_addr", - ".", - "ifname;", - "flags", - "dynamic;", - "counter;", - "size", - "65535;", - "}", - ], - ) { - error!("Unable to setup counter tables with {:?}", e); - } - } +fn create_nat_table(ex_nic: &str) -> Result<(), KernelInterfaceError> { + // create the table + run_command("nft", &["create", "table", "ip", "nat"])?; - fn create_nat_table(&self, ex_nic: &str) -> Result<(), KernelInterfaceError> { - // create the table - self.run_command("nft", &["create", "table", "ip", "nat"])?; + // Add a chain to the table + run_command( + "nft", + &[ + "create", + "chain", + "ip", + "nat", + "postrouting", + "{", + "type", + "nat", + "hook", + "postrouting", + "priority", + "100", + ";", + "policy", + "accept", + ";", + "}", + ], + )?; - // Add a chain to the table - self.run_command( - "nft", - &[ - "create", - "chain", - "ip", - "nat", - "postrouting", - "{", - "type", - "nat", - "hook", - "postrouting", - "priority", - "100", - ";", - "policy", - "accept", - ";", - "}", - ], - )?; + // Add rule to chain + run_command( + "nft", + &[ + "add", + "rule", + "ip", + "nat", + "postrouting", + "oifname", + ex_nic, + "masquerade", + ], + )?; - // Add rule to chain - self.run_command( - "nft", - &[ - "add", - "rule", - "ip", - "nat", - "postrouting", - "oifname", - ex_nic, - "masquerade", - ], - )?; + Ok(()) +} - Ok(()) +fn is_lan_fwd_present() -> Result { + let out = run_command("nft", &["list", "chain", "inet", "fw4", "forward_lan"])?; + let out = out.stdout; + let out = String::from_utf8(out).expect("fix command"); + for line in out.lines() { + if line.trim() == "accept" { + return Ok(true); + } } - fn is_lan_fwd_present(&self) -> Result { - let out = self.run_command("nft", &["list", "chain", "inet", "fw4", "forward_lan"])?; - let out = out.stdout; - let out = String::from_utf8(out).expect("fix command"); - for line in out.lines() { - if line.trim() == "accept" { - return Ok(true); + Ok(false) +} + +fn get_reject_rule_handle() -> Result, KernelInterfaceError> { + let out = run_command( + "nft", + &["-a", "list", "chain", "inet", "fw4", "forward_lan"], + )?; + let out = out.stdout; + let out = String::from_utf8(out).expect("fix command"); + for line in out.lines() { + if line.contains("reject") { + let handle: Vec<&str> = line.split(' ').collect(); + match handle.last() { + Some(a) => match (*a).parse() { + Ok(b) => return Ok(Some(b)), + Err(_) => { + return Ok(None); + } + }, + None => return Ok(None), } } + } + Ok(None) +} - Ok(false) +fn is_nat_table_present() -> Result { + let out = run_command("nft", &["list", "table", "ip", "nat"])?; + if out.status.success() { + return Ok(true); } + Ok(false) +} - fn get_reject_rule_handle(&self) -> Result, KernelInterfaceError> { - let out = self.run_command( +pub fn insert_reject_rule() -> Result<(), KernelInterfaceError> { + if get_reject_rule_handle()?.is_none() { + run_command( "nft", - &["-a", "list", "chain", "inet", "fw4", "forward_lan"], + &["insert", "rule", "inet", "fw4", "forward_lan", "reject"], )?; - let out = out.stdout; - let out = String::from_utf8(out).expect("fix command"); - for line in out.lines() { - if line.contains("reject") { - let handle: Vec<&str> = line.split(' ').collect(); - match handle.last() { - Some(a) => match (*a).parse() { - Ok(b) => return Ok(Some(b)), - Err(_) => { - return Ok(None); - } - }, - None => return Ok(None), - } - } - } - Ok(None) } + Ok(()) +} - fn is_nat_table_present(&self) -> Result { - let out = self.run_command("nft", &["list", "table", "ip", "nat"])?; - if out.status.success() { - return Ok(true); - } - Ok(false) +pub fn delete_reject_rule() -> Result<(), KernelInterfaceError> { + if let Some(handle) = get_reject_rule_handle()? { + run_command( + "nft", + &[ + "delete", + "rule", + "inet", + "fw4", + "forward_lan", + "handle", + &handle.to_string(), + ], + )?; } + Ok(()) +} - pub fn insert_reject_rule(&self) -> Result<(), KernelInterfaceError> { - if self.get_reject_rule_handle()?.is_none() { - self.run_command( - "nft", - &["insert", "rule", "inet", "fw4", "forward_lan", "reject"], - )?; - } - Ok(()) +pub fn init_nat_chain(ex_nic: &str) -> Result<(), KernelInterfaceError> { + if !is_nat_table_present()? { + create_nat_table(ex_nic)?; } + Ok(()) +} - pub fn delete_reject_rule(&self) -> Result<(), KernelInterfaceError> { - if let Some(handle) = self.get_reject_rule_handle()? { - self.run_command( - "nft", - &[ - "delete", - "rule", - "inet", - "fw4", - "forward_lan", - "handle", - &handle.to_string(), - ], - )?; - } - Ok(()) +pub fn set_nft_lan_fwd_rule() -> Result<(), KernelInterfaceError> { + if !is_lan_fwd_present()? { + create_fwd_rule()?; } - pub fn init_nat_chain(&self, ex_nic: &str) -> Result<(), KernelInterfaceError> { - if !self.is_nat_table_present()? { - self.create_nat_table(ex_nic)?; - } - Ok(()) - } + Ok(()) +} - pub fn set_nft_lan_fwd_rule(&self) -> Result<(), KernelInterfaceError> { - if !self.is_lan_fwd_present()? { - self.create_fwd_rule()?; +fn are_nft_exit_forward_rules_present( + interface: &str, + ex_nic: &str, +) -> Result { + let out = run_command("nft", &["list", "chain", "inet", "fw4", "forward"])?; + let out = out.stdout; + let out = String::from_utf8(out).expect("fix command"); + for line in out.lines() { + // If one is present, they should all be present + if line.contains(&format!("iifname {interface} oifname {ex_nic}")) { + return Ok(true); } - - Ok(()) } + Ok(false) +} - fn are_nft_exit_forward_rules_present( - &self, - interface: &str, - ex_nic: &str, - ) -> Result { - let out = self.run_command("nft", &["list", "chain", "inet", "fw4", "forward"])?; - let out = out.stdout; - let out = String::from_utf8(out).expect("fix command"); - for line in out.lines() { - // If one is present, they should all be present - if line.contains(&format!("iifname {interface} oifname {ex_nic}")) { - return Ok(true); - } - } - Ok(false) - } +pub fn insert_nft_exit_forward_rules( + interface: &str, + ex_nic: &str, + external_v6: Option<(IpAddr, u8)>, +) -> Result<(), KernelInterfaceError> { + // Packets from wg_exit -> ex_nic. Add this rule for both v4 and v6 traffic + // nft add rule ip tests forward iifname "wg_exit" oifname "ex_nic" counter accept + if !are_nft_exit_forward_rules_present(interface, ex_nic)? { + run_command( + "nft", + &[ + "add", "rule", "inet", "fw4", "forward", "iifname", interface, "oifname", ex_nic, + "counter", "accept", + ], + )?; + // v4 packets from ex_nic -> wg_exit + // nft add rule ip tests forward iifname "ex_nic" oifname "wg_exit" ct state related,established counter accept + run_command( + "nft", + &[ + "add", + "rule", + "inet", + "fw4", + "forward", + "meta", + "nfproto", + "ipv4", + "iifname", + ex_nic, + "oifname", + interface, + "ct", + "state", + "related,established", + "counter", + "accept", + ], + )?; - pub fn insert_nft_exit_forward_rules( - &self, - interface: &str, - ex_nic: &str, - external_v6: Option<(IpAddr, u8)>, - ) -> Result<(), KernelInterfaceError> { - // Packets from wg_exit -> ex_nic. Add this rule for both v4 and v6 traffic - // nft add rule ip tests forward iifname "wg_exit" oifname "ex_nic" counter accept - if !self.are_nft_exit_forward_rules_present(interface, ex_nic)? { - self.run_command( - "nft", - &[ - "add", "rule", "inet", "fw4", "forward", "iifname", interface, "oifname", - ex_nic, "counter", "accept", - ], - )?; - // v4 packets from ex_nic -> wg_exit - // nft add rule ip tests forward iifname "ex_nic" oifname "wg_exit" ct state related,established counter accept - self.run_command( + // v6 packets from ex_nic -> wg_exit + if let Some((external_ip_v6, netmask_v6)) = external_v6 { + run_command( "nft", &[ "add", @@ -237,124 +257,95 @@ impl dyn KernelInterface { "inet", "fw4", "forward", - "meta", - "nfproto", - "ipv4", "iifname", ex_nic, "oifname", interface, - "ct", - "state", - "related,established", + "ip", + "daddr", + &format!("{}/{}", external_ip_v6, netmask_v6), "counter", "accept", ], )?; - - // v6 packets from ex_nic -> wg_exit - if let Some((external_ip_v6, netmask_v6)) = external_v6 { - self.run_command( - "nft", - &[ - "add", - "rule", - "inet", - "fw4", - "forward", - "iifname", - ex_nic, - "oifname", - interface, - "ip", - "daddr", - &format!("{}/{}", external_ip_v6, netmask_v6), - "counter", - "accept", - ], - )?; - } } - - Ok(()) } - pub fn nft_init_counters( - &self, - set_name: &str, - chain: &str, - interface: &str, - ) -> Result<(), KernelInterfaceError> { - if !self.is_nft_set_present(set_name) { - // add set - self.create_nft_set(set_name); - // add insert chain - self.create_nft_insert_chain(set_name, chain, interface); - } - Ok(()) + Ok(()) +} + +pub fn nft_init_counters( + set_name: &str, + chain: &str, + interface: &str, +) -> Result<(), KernelInterfaceError> { + if !is_nft_set_present(set_name) { + // add set + create_nft_set(set_name); + // add insert chain + create_nft_insert_chain(set_name, chain, interface); } + Ok(()) +} - fn is_nft_set_present(&self, set_name: &str) -> bool { - let out = self - .run_command("nft", &["list", "set", "inet", "fw4", set_name]) - .expect("Fix command!"); - if out.status.success() { - return true; - } - false +fn is_nft_set_present(set_name: &str) -> bool { + let out = run_command("nft", &["list", "set", "inet", "fw4", set_name]).expect("Fix command!"); + if out.status.success() { + return true; } + false +} - fn create_nft_insert_chain(&self, set_name: &str, chain: &str, interface: &str) { - // Add rule to match on a set element counter to increment it - if let Err(e) = self.run_command( - "nft", - &[ - "insert", - "rule", - "inet", - "fw4", - chain, - "ip6", - "daddr", - ".", - "meta", - interface, - &("@".to_owned() + set_name), - ], - ) { - error!("Unable to setup counters: {}", e); - } +fn create_nft_insert_chain(set_name: &str, chain: &str, interface: &str) { + // Add rule to match on a set element counter to increment it + if let Err(e) = run_command( + "nft", + &[ + "insert", + "rule", + "inet", + "fw4", + chain, + "ip6", + "daddr", + ".", + "meta", + interface, + &("@".to_owned() + set_name), + ], + ) { + error!("Unable to setup counters: {}", e); + } - // insert ip6addr . ifname when not present in set - // sudo nft -e -j insert rule filter input ip daddr . meta iifname != @myset3 set add ip saddr . meta iifname @myset3 - if let Err(e) = self.run_command( - "nft", - &vec![ - "insert", - "rule", - "inet", - "fw4", - chain, - "ip6", - "daddr", - ".", - "meta", - interface, - "!=", - &("@".to_owned() + set_name), - "add", - &("@".to_owned() + set_name), - "{", - "ip6", - "daddr", - ".", - "meta", - interface, - "counter", - "}", - ], - ) { - error!("Unable to setup counters: {}", e); - } + // insert ip6addr . ifname when not present in set + // sudo nft -e -j insert rule filter input ip daddr . meta iifname != @myset3 set add ip saddr . meta iifname @myset3 + if let Err(e) = run_command( + "nft", + &vec![ + "insert", + "rule", + "inet", + "fw4", + chain, + "ip6", + "daddr", + ".", + "meta", + interface, + "!=", + &("@".to_owned() + set_name), + "add", + &("@".to_owned() + set_name), + "{", + "ip6", + "daddr", + ".", + "meta", + interface, + "counter", + "}", + ], + ) { + error!("Unable to setup counters: {}", e); } } diff --git a/althea_kernel_interface/src/netns.rs b/althea_kernel_interface/src/netns.rs index 565f9a30f..e94a0e944 100644 --- a/althea_kernel_interface/src/netns.rs +++ b/althea_kernel_interface/src/netns.rs @@ -1,65 +1,63 @@ -use crate::KernelInterface; +use crate::run_command; use std::thread::sleep; -impl dyn KernelInterface { - /// Custom function for our integration test environment, returns a numbered netnamespace - /// this thread is currently operating in, allowing us to dispatch lazy static data - /// independent to each thread. Arch wise the fact that we have this problem at all indicates - /// that the lazy static for cross thread comms arch if a bit questionable by nature - pub fn check_integration_test_netns(&self) -> u32 { - if cfg!(feature = "integration_test") { - let mut ns = self.run_command("ip", &["netns", "identify"]); - while let Err(e) = ns { - warn!("Could not get netns name, retrying: {:?}", e); - sleep(std::time::Duration::from_secs(1)); - ns = self.run_command("ip", &["netns", "identify"]); - } - let ns = ns.unwrap(); - let ns = match String::from_utf8(ns.stdout) { - Ok(s) => s, - Err(_) => panic!("Could not get netns name!"), - }; - let ns = ns.trim(); - match ( - ns.split('-').last().unwrap().parse(), - ns.split('_').last().unwrap().parse(), - ) { - (Ok(a), _) => a, - (_, Ok(a)) => a, - (Err(_), Err(_)) => { - // for some reason it's not easily possible to tell if we're in a unit test - error!("Could not get netns name, maybe a unit test?"); - 0 - } +/// Custom function for our integration test environment, returns a numbered netnamespace +/// this thread is currently operating in, allowing us to dispatch lazy static data +/// independent to each thread. Arch wise the fact that we have this problem at all indicates +/// that the lazy static for cross thread comms arch if a bit questionable by nature +pub fn check_integration_test_netns() -> u32 { + if cfg!(feature = "integration_test") { + let mut ns = run_command("ip", &["netns", "identify"]); + while let Err(e) = ns { + warn!("Could not get netns name, retrying: {:?}", e); + sleep(std::time::Duration::from_secs(1)); + ns = run_command("ip", &["netns", "identify"]); + } + let ns = ns.unwrap(); + let ns = match String::from_utf8(ns.stdout) { + Ok(s) => s, + Err(_) => panic!("Could not get netns name!"), + }; + let ns = ns.trim(); + match ( + ns.split('-').last().unwrap().parse(), + ns.split('_').last().unwrap().parse(), + ) { + (Ok(a), _) => a, + (_, Ok(a)) => a, + (Err(_), Err(_)) => { + // for some reason it's not easily possible to tell if we're in a unit test + error!("Could not get netns name, maybe a unit test?"); + 0 } - } else { - 0 } + } else { + 0 } +} - /// Gets the network namespace name that holds the thread this function was called from. - /// If the calling thread was not inside a network namespace/in the default namespace, this - /// function returns a None - pub fn get_namespace(&self) -> Option { - let output = match self.run_command("ip", &["netns", "identify"]) { - Ok(output) => output, - Err(_) => { - warn!("Could not run ip netns- is ip netns installed?"); - return None; - } - }; - match String::from_utf8(output.stdout) { - Ok(mut s) => { - s.truncate(s.len() - 1); - if !s.is_empty() { - return Some(s); - } - None - } - Err(_) => { - warn!("Could not get ip netns name from stdout!"); - None +/// Gets the network namespace name that holds the thread this function was called from. +/// If the calling thread was not inside a network namespace/in the default namespace, this +/// function returns a None +pub fn get_namespace() -> Option { + let output = match run_command("ip", &["netns", "identify"]) { + Ok(output) => output, + Err(_) => { + warn!("Could not run ip netns- is ip netns installed?"); + return None; + } + }; + match String::from_utf8(output.stdout) { + Ok(mut s) => { + s.truncate(s.len() - 1); + if !s.is_empty() { + return Some(s); } + None + } + Err(_) => { + warn!("Could not get ip netns name from stdout!"); + None } } } diff --git a/althea_kernel_interface/src/open_tunnel.rs b/althea_kernel_interface/src/open_tunnel.rs index 1b4efc568..5d1b6b61f 100644 --- a/althea_kernel_interface/src/open_tunnel.rs +++ b/althea_kernel_interface/src/open_tunnel.rs @@ -1,4 +1,6 @@ -use crate::{DefaultRoute, KernelInterface, KernelInterfaceError}; +use crate::ip_route::manual_peers_route; +use crate::link_local_tools::get_device_name; +use crate::{run_command, DefaultRoute, KernelInterfaceError}; use althea_types::WgKey; use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use std::path::Path; @@ -56,229 +58,101 @@ pub struct TunnelOpenArgs<'a> { pub settings_default_route: &'a mut Option, } -impl dyn KernelInterface { - pub fn open_tunnel(&self, args: TunnelOpenArgs) -> Result<(), KernelInterfaceError> { - let (setup_gateway_routes, socket_connect_str) = match args.endpoint { - SocketAddr::V4(sockv4) => ( - // if the ipv4 address is not private we are connecting to an exit - // and instead we are connecting to a local peer over ipv4 - !sockv4.ip().is_private(), - format!("{}:{}", sockv4.ip(), sockv4.port()), - ), - SocketAddr::V6(sockv6) => { - let is_link_local = is_link_local(IpAddr::V6(*sockv6.ip())); - if is_link_local { - // if the ipv6 address is link local we need to add the interface name - // to the endpoint - let interface_name = self.get_device_name((*sockv6.ip()).into())?; - ( - false, - format!("[{}%{}]:{}", sockv6.ip(), interface_name, sockv6.port()), - ) - } else { - // ipv6 is not link local, it's over the internet - (true, format!("[{}]:{}", sockv6.ip(), sockv6.port())) - } +pub fn open_tunnel(args: TunnelOpenArgs) -> Result<(), KernelInterfaceError> { + let (setup_gateway_routes, socket_connect_str) = match args.endpoint { + SocketAddr::V4(sockv4) => ( + // if the ipv4 address is not private we are connecting to an exit + // and instead we are connecting to a local peer over ipv4 + !sockv4.ip().is_private(), + format!("{}:{}", sockv4.ip(), sockv4.port()), + ), + SocketAddr::V6(sockv6) => { + let is_link_local = is_link_local(IpAddr::V6(*sockv6.ip())); + if is_link_local { + // if the ipv6 address is link local we need to add the interface name + // to the endpoint + let interface_name = get_device_name((*sockv6.ip()).into())?; + ( + false, + format!("[{}%{}]:{}", sockv6.ip(), interface_name, sockv6.port()), + ) + } else { + // ipv6 is not link local, it's over the internet + (true, format!("[{}]:{}", sockv6.ip(), sockv6.port())) } - }; - - let allowed_addresses = "::/0".to_string(); - - trace!("socket connect string: {}", socket_connect_str); - let output = self.run_command( - "wg", - &[ - "set", - &args.interface, - "listen-port", - &format!("{}", args.port), - "private-key", - args.private_key_path.to_str().unwrap(), - "peer", - &format!("{}", args.remote_pub_key), - "endpoint", - &socket_connect_str, - "allowed-ips", - &allowed_addresses, - "persistent-keepalive", - "5", - ], - )?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error from wg command: {}", - String::from_utf8(output.stderr)? - ))); - } - let _output = self.run_command( - "ip", - &[ - "address", - "add", - &args.own_ip.to_string(), - "dev", - &args.interface, - ], - )?; - - // Add second ip to tunnel used only by exits currently - if let Some(ip) = args.own_ip_v2 { - let _output = self.run_command( - "ip", - &["address", "add", &ip.to_string(), "dev", &args.interface], - )?; } + }; - // add ipv6 link local slacc address manually, this is required for peer discovery - self.run_command( + let allowed_addresses = "::/0".to_string(); + + trace!("socket connect string: {}", socket_connect_str); + let output = run_command( + "wg", + &[ + "set", + &args.interface, + "listen-port", + &format!("{}", args.port), + "private-key", + args.private_key_path.to_str().unwrap(), + "peer", + &format!("{}", args.remote_pub_key), + "endpoint", + &socket_connect_str, + "allowed-ips", + &allowed_addresses, + "persistent-keepalive", + "5", + ], + )?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error from wg command: {}", + String::from_utf8(output.stderr)? + ))); + } + let _output = run_command( + "ip", + &[ + "address", + "add", + &args.own_ip.to_string(), + "dev", + &args.interface, + ], + )?; + + // Add second ip to tunnel used only by exits currently + if let Some(ip) = args.own_ip_v2 { + let _output = run_command( "ip", - &[ - "address", - "add", - &format!("{}/64", to_wg_local(&args.own_ip)), - "dev", - &args.interface, - ], + &["address", "add", &ip.to_string(), "dev", &args.interface], )?; - - if setup_gateway_routes { - self.manual_peers_route(&args.endpoint.ip(), args.settings_default_route)?; - } - - let output = self.run_command("ip", &["link", "set", "dev", &args.interface, "up"])?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error setting wg interface {:?} up: {}", - args, - String::from_utf8(output.stderr)? - ))); - } - Ok(()) } -} - -#[test] -fn test_open_tunnel_linux() { - use crate::KI; - - use crate::ip_route::DefaultRoute; - use std::net::SocketAddrV6; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - let interface = String::from("wg1"); - let endpoint_link_local_ip = Ipv6Addr::new(0xfe80, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x90); - let own_mesh_ip = "fd00::1".parse::().unwrap(); - let endpoint = SocketAddr::V6(SocketAddrV6::new(endpoint_link_local_ip, 8088, 0, 123)); - let remote_pub_key = "x8AcR9wI4t97aowYFlis077BDBk9SLdq6khMiixuTsQ=" - .parse() - .unwrap(); - let private_key_path = Path::new("private_key"); - - let wg_args = &[ - "set", - "wg1", - "listen-port", - "8088", - "private-key", - "private_key", - "peer", - "x8AcR9wI4t97aowYFlis077BDBk9SLdq6khMiixuTsQ=", - "endpoint", - "[fe80::12:34:56:78:90%eth2]:8088", - "allowed-ips", - "::/0", - "persistent-keepalive", - "5", - ]; - - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - //get interfaces - assert_eq!(program, "ip"); - assert_eq!(args, &["neigh"]); - - Ok(Output { - stdout: b"10.0.2.2 dev eth0 lladdr 00:00:00:aa:00:03 STALE -10.0.0.2 dev eth0 FAILED -10.0.1.2 dev eth0 lladdr 00:00:00:aa:00:05 REACHABLE -2001::2 dev eth0 lladdr 00:00:00:aa:00:56 REACHABLE -fe80:0:0:12:34:56:78:90 dev eth2 lladdr 76:59:8e:98:00:81 STALE -fe80::433:25ff:fe8c:e1ea dev eth0 lladdr 1a:32:06:78:05:0a STALE -2001::2 dev eth0 FAILED" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 2 => { - // setup wg interface - assert_eq!(program, "wg"); - assert_eq!(args, wg_args); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 3 => { - // add global ip - assert_eq!(program, "ip"); - assert_eq!(args, ["address", "add", "fd00::1", "dev", "wg1"]); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 4 => { - // add link local ip - assert_eq!(program, "ip"); - assert_eq!(args, ["address", "add", "fe80::1/64", "dev", "wg1"]); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - 5 => { - // bring if up - assert_eq!(program, "ip"); - assert_eq!(args, ["link", "set", "dev", "wg1", "up"]); - Ok(Output { - stdout: b"".to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => unimplemented!(), - } - })); - - let def_route = DefaultRoute { - via: "192.168.8.1".parse().unwrap(), - nic: "wifiinterface".to_string(), - proto: Some("dhcp".to_string()), - metric: Some(600), - src: None, - }; - - let args = TunnelOpenArgs { - interface, - port: 8088, - endpoint, - remote_pub_key, - private_key_path, - own_ip: own_mesh_ip, - own_ip_v2: None, - external_nic: None, - settings_default_route: &mut Some(def_route), - }; + // add ipv6 link local slacc address manually, this is required for peer discovery + run_command( + "ip", + &[ + "address", + "add", + &format!("{}/64", to_wg_local(&args.own_ip)), + "dev", + &args.interface, + ], + )?; + + if setup_gateway_routes { + manual_peers_route(&args.endpoint.ip(), args.settings_default_route)?; + } - KI.open_tunnel(args).unwrap(); + let output = run_command("ip", &["link", "set", "dev", &args.interface, "up"])?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error setting wg interface {:?} up: {}", + args, + String::from_utf8(output.stderr)? + ))); + } + Ok(()) } diff --git a/althea_kernel_interface/src/openwrt_ubus.rs b/althea_kernel_interface/src/openwrt_ubus.rs index b798e53ae..1e4e030ee 100644 --- a/althea_kernel_interface/src/openwrt_ubus.rs +++ b/althea_kernel_interface/src/openwrt_ubus.rs @@ -1,18 +1,12 @@ -use crate::KernelInterface; -use crate::KernelInterfaceError; +use crate::{run_command, KernelInterfaceError}; -impl dyn KernelInterface { - /// calls a ubus rpc - pub fn ubus_call( - &self, - namespace: &str, - function: &str, - argument: &str, - ) -> Result { - let output = String::from_utf8( - self.run_command("ubus", &["call", namespace, function, argument])? - .stdout, - )?; - Ok(output) - } +/// calls a ubus rpc +pub fn ubus_call( + namespace: &str, + function: &str, + argument: &str, +) -> Result { + let output = + String::from_utf8(run_command("ubus", &["call", namespace, function, argument])?.stdout)?; + Ok(output) } diff --git a/althea_kernel_interface/src/ping_check.rs b/althea_kernel_interface/src/ping_check.rs index 3b717dee5..40d17f7eb 100644 --- a/althea_kernel_interface/src/ping_check.rs +++ b/althea_kernel_interface/src/ping_check.rs @@ -1,38 +1,34 @@ -use crate::KernelInterface; use crate::KernelInterfaceError; use oping::Ping; use std::net::IpAddr; use std::time::Duration; -impl dyn KernelInterface { - //Pings an address to determine if it's online - pub fn ping_check( - &self, - ip: &IpAddr, - timeout: Duration, - outgoing_device: Option<&str>, - ) -> Result { - trace!("starting ping"); - let mut ping = Ping::new(); - ping.add_host(&ip.to_string())?; +//Pings an address to determine if it's online +pub fn ping_check( + ip: &IpAddr, + timeout: Duration, + outgoing_device: Option<&str>, +) -> Result { + trace!("starting ping"); + let mut ping = Ping::new(); + ping.add_host(&ip.to_string())?; - // set outgoing device as needed - if let Some(device) = outgoing_device { - ping.set_device(device)?; - } + // set outgoing device as needed + if let Some(device) = outgoing_device { + ping.set_device(device)?; + } - ping.set_timeout(timeout.as_millis() as f64 / 1000f64)?; - trace!("sending ping"); - let mut response = ping.send()?; - trace!("sent ping"); - if let Some(res) = response.next() { - trace!("got ping response {:?}", res); - // we get dropped '1' to mean the packet is dropped - // because this create offers c bindings and doesn't do - // much of anything to adapt them - Ok(res.dropped == 0) - } else { - Ok(false) - } + ping.set_timeout(timeout.as_millis() as f64 / 1000f64)?; + trace!("sending ping"); + let mut response = ping.send()?; + trace!("sent ping"); + if let Some(res) = response.next() { + trace!("got ping response {:?}", res); + // we get dropped '1' to mean the packet is dropped + // because this create offers c bindings and doesn't do + // much of anything to adapt them + Ok(res.dropped == 0) + } else { + Ok(false) } } diff --git a/althea_kernel_interface/src/set_system_password.rs b/althea_kernel_interface/src/set_system_password.rs index 5d80b368b..f837a33d2 100644 --- a/althea_kernel_interface/src/set_system_password.rs +++ b/althea_kernel_interface/src/set_system_password.rs @@ -1,36 +1,33 @@ -use crate::KernelInterface; use crate::KernelInterfaceError; use std::io::Write; use std::process::{Command, Stdio}; -impl dyn KernelInterface { - /// Sets the system password on openwrt - pub fn set_system_password(&self, password: String) -> Result<(), KernelInterfaceError> { - trace!("Trying to set the system password to {}", password); - let mut password_with_newline = password.as_bytes().to_vec(); - password_with_newline.push(b'\n'); +/// Sets the system password on openwrt +pub fn set_system_password(password: String) -> Result<(), KernelInterfaceError> { + trace!("Trying to set the system password to {}", password); + let mut password_with_newline = password.as_bytes().to_vec(); + password_with_newline.push(b'\n'); - let mut passwd = Command::new("passwd") - .args(["-a sha512"]) - .stdout(Stdio::piped()) - .stdin(Stdio::piped()) - .spawn() - .unwrap(); + let mut passwd = Command::new("passwd") + .args(["-a sha512"]) + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .spawn() + .unwrap(); - passwd - .stdin - .as_mut() - .unwrap() - .write_all(&password_with_newline)?; - passwd - .stdin - .as_mut() - .unwrap() - .write_all(&password_with_newline)?; + passwd + .stdin + .as_mut() + .unwrap() + .write_all(&password_with_newline)?; + passwd + .stdin + .as_mut() + .unwrap() + .write_all(&password_with_newline)?; - let output = passwd.wait_with_output()?; - trace!("Got {} from passwd", String::from_utf8(output.stdout)?); + let output = passwd.wait_with_output()?; + trace!("Got {} from passwd", String::from_utf8(output.stdout)?); - Ok(()) - } + Ok(()) } diff --git a/althea_kernel_interface/src/setup_wg_if.rs b/althea_kernel_interface/src/setup_wg_if.rs index 3aed3a7b0..cb5635cf3 100644 --- a/althea_kernel_interface/src/setup_wg_if.rs +++ b/althea_kernel_interface/src/setup_wg_if.rs @@ -1,155 +1,161 @@ -use crate::{KernelInterface, KernelInterfaceError, KernelInterfaceError as Error}; +use crate::interface_tools::get_interfaces; +use crate::{run_command, KernelInterfaceError, KernelInterfaceError as Error}; use althea_types::WgKey; use std::str::from_utf8; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -impl dyn KernelInterface { - pub fn get_peers(&self, iface_name: &str) -> Result, Error> { - let output = self.run_command("wg", &["show", iface_name, "peers"])?; +pub fn get_peers(iface_name: &str) -> Result, Error> { + let output = run_command("wg", &["show", iface_name, "peers"])?; - let output = from_utf8(&output.stdout)?; + let output = from_utf8(&output.stdout)?; - let mut peers = Vec::new(); + let mut peers = Vec::new(); - for l in output.lines() { - let parsed = l.parse(); - if let Ok(val) = parsed { - peers.push(val); - } else { - warn!("Could not parse peer! {}", l); - } + for l in output.lines() { + let parsed = l.parse(); + if let Ok(val) = parsed { + peers.push(val); + } else { + warn!("Could not parse peer! {}", l); } - - Ok(peers) } - /// checks the existing interfaces to find an interface name that isn't in use. - /// then calls iproute2 to set up a new interface with that name - pub fn create_blank_wg_numbered_wg_interface(&self) -> Result { - // this is the maximum allowed retries for when an interface is claimed to have already existed - // since we only setup interfaces once this can only happen if we have lost an interface or if - // the kernel is acting strange, either way it's better just to skip that interface and wait - // on a Rita restart to clean it up some day. - const MAX_RETRY: u8 = 5; - - //call "ip links" to get a list of currently set up links - let links = self.get_interfaces()?; - let mut if_num = 0; - //loop through the output of "ip links" until we find a wg suffix that isn't taken (e.g. "wg3") - while links.contains(&format!("wg{if_num}")) { - if_num += 1; - } - - let mut count = 0; - let mut interface = format!("wg{if_num}"); - let mut res = self.create_blank_wg_interface(&interface); - while let Err(KernelInterfaceError::WgExistsError) = res { - if_num += 1; - interface = format!("wg{if_num}"); - res = self.create_blank_wg_interface(&interface); - count += 1; - if count > MAX_RETRY { - break; - } - } + Ok(peers) +} - res?; - Ok(interface) +/// checks the existing interfaces to find an interface name that isn't in use. +/// then calls iproute2 to set up a new interface with that name +pub fn create_blank_wg_numbered_wg_interface() -> Result { + // this is the maximum allowed retries for when an interface is claimed to have already existed + // since we only setup interfaces once this can only happen if we have lost an interface or if + // the kernel is acting strange, either way it's better just to skip that interface and wait + // on a Rita restart to clean it up some day. + const MAX_RETRY: u8 = 5; + + //call "ip links" to get a list of currently set up links + let links = get_interfaces()?; + let mut if_num = 0; + //loop through the output of "ip links" until we find a wg suffix that isn't taken (e.g. "wg3") + while links.contains(&format!("wg{if_num}")) { + if_num += 1; } - /// calls iproute2 to set up a new interface with a given name. - pub fn create_blank_wg_interface(&self, name: &str) -> Result<(), KernelInterfaceError> { - let output = self.run_command("ip", &["link", "add", name, "type", "wireguard"])?; - let stderr = String::from_utf8(output.stderr)?; - if !stderr.is_empty() { - if stderr.contains("exists") { - return Err(KernelInterfaceError::WgExistsError); - } else { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error adding wg link: {stderr}" - ))); - } + let mut count = 0; + let mut interface = format!("wg{if_num}"); + let mut res = create_blank_wg_interface(&interface); + while let Err(KernelInterfaceError::WgExistsError) = res { + if_num += 1; + interface = format!("wg{if_num}"); + res = create_blank_wg_interface(&interface); + count += 1; + if count > MAX_RETRY { + break; } - - Ok(()) } - /// Returns the number of clients that are active on the wg_exit tunnel - pub fn get_wg_exit_clients_online(&self, interface: &str) -> Result { - let output = self.run_command("wg", &["show", interface, "latest-handshakes"])?; - let mut num: u32 = 0; - let out = String::from_utf8(output.stdout)?; - for line in out.lines() { - let content: Vec<&str> = line.split('\t').collect(); - let mut itr = content.iter(); - itr.next(); - let timestamp = itr.next().ok_or_else(|| { - KernelInterfaceError::RuntimeError("Option did not contain a value.".to_string()) - })?; - let d = UNIX_EPOCH + Duration::from_secs(timestamp.parse()?); - - if SystemTime::now().duration_since(d)? < Duration::new(600, 0) { - num += 1; - } + res?; + Ok(interface) +} + +/// calls iproute2 to set up a new interface with a given name. +pub fn create_blank_wg_interface(name: &str) -> Result<(), KernelInterfaceError> { + let output = run_command("ip", &["link", "add", name, "type", "wireguard"])?; + let stderr = String::from_utf8(output.stderr)?; + if !stderr.is_empty() { + if stderr.contains("exists") { + return Err(KernelInterfaceError::WgExistsError); + } else { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error adding wg link: {stderr}" + ))); } - Ok(num) } - /// Returns the last handshake time of every client on this tunnel. - pub fn get_last_handshake_time(&self, ifname: &str) -> Result, Error> { - let output = self.run_command("wg", &["show", ifname, "latest-handshakes"])?; - let out = String::from_utf8(output.stdout)?; - let mut timestamps = Vec::new(); - for line in out.lines() { - let content: Vec<&str> = line.split('\t').collect(); - let mut itr = content.iter(); - let wg_key: WgKey = match itr.next() { - Some(val) => val.parse()?, - None => { - return Err(KernelInterfaceError::RuntimeError( - "Invalid line!".to_string(), - )) - } - }; - let timestamp = match itr.next() { - Some(val) => val.parse()?, - None => { - return Err(KernelInterfaceError::RuntimeError( - "Invalid line!".to_string(), - )) - } - }; - let d = UNIX_EPOCH + Duration::from_secs(timestamp); - timestamps.push((wg_key, d)) + Ok(()) +} + +/// internal helper function for testing get_wg_exit_clients_online +fn get_wg_exit_clients_online_internal(out: String) -> Result { + let mut num: u32 = 0; + for line in out.lines() { + let content: Vec<&str> = line.split('\t').collect(); + let mut itr = content.iter(); + itr.next(); + let timestamp = itr.next().ok_or_else(|| { + KernelInterfaceError::RuntimeError("Option did not contain a value.".to_string()) + })?; + let d = UNIX_EPOCH + Duration::from_secs(timestamp.parse()?); + + if SystemTime::now().duration_since(d)? < Duration::new(600, 0) { + num += 1; } - Ok(timestamps) } + Ok(num) +} - /// Returns the last handshake time of every ACTIVE client on this tunnel. - /// An active handshake mean a wireguard tunnel that has a latest handshake value - /// When running wg show wg_exit latest-handshake, a entries with timestamp 0 are inactive - pub fn get_last_active_handshake_time( - &self, - ifname: &str, - ) -> Result, Error> { - let timestamps = self.get_last_handshake_time(ifname)?; - let timestamps = timestamps - .into_iter() - .filter(|(_, time)| *time != UNIX_EPOCH) - .collect(); - Ok(timestamps) +/// Returns the number of clients that are active on the wg_exit tunnel +pub fn get_wg_exit_clients_online(interface: &str) -> Result { + let output = run_command("wg", &["show", interface, "latest-handshakes"])?; + let out = String::from_utf8(output.stdout)?; + get_wg_exit_clients_online_internal(out) +} + +/// Internal helper function for ci testing get_last_handshake_time +fn get_last_active_handshake_time_internal(out: String) -> Result, Error> { + let mut timestamps = Vec::new(); + for line in out.lines() { + let content: Vec<&str> = line.split('\t').collect(); + let mut itr = content.iter(); + let wg_key: WgKey = match itr.next() { + Some(val) => val.parse()?, + None => { + return Err(KernelInterfaceError::RuntimeError( + "Invalid line!".to_string(), + )) + } + }; + let timestamp = match itr.next() { + Some(val) => val.parse()?, + None => { + return Err(KernelInterfaceError::RuntimeError( + "Invalid line!".to_string(), + )) + } + }; + let d = UNIX_EPOCH + Duration::from_secs(timestamp); + timestamps.push((wg_key, d)) } + Ok(timestamps) +} - /// Gets a list of all active wireguard interfaces on this device - pub fn get_list_of_wireguard_interfaces(&self) -> Result, Error> { - let output = self.run_command("wg", &["show", "interfaces"])?; - let out = String::from_utf8(output.stdout)?; - let mut interfaces = Vec::new(); - for interface in out.split_ascii_whitespace() { - interfaces.push(interface.to_string()) - } - Ok(interfaces) +/// Returns the last handshake time of every client on this tunnel. +pub fn get_last_handshake_time(ifname: &str) -> Result, Error> { + let output = run_command("wg", &["show", ifname, "latest-handshakes"])?; + let out = String::from_utf8(output.stdout)?; + get_last_active_handshake_time_internal(out) +} + +/// Returns the last handshake time of every ACTIVE client on this tunnel. +/// An active handshake mean a wireguard tunnel that has a latest handshake value +/// When running wg show wg_exit latest-handshake, a entries with timestamp 0 are inactive +pub fn get_last_active_handshake_time(ifname: &str) -> Result, Error> { + let timestamps = get_last_handshake_time(ifname)?; + let timestamps = timestamps + .into_iter() + .filter(|(_, time)| *time != UNIX_EPOCH) + .collect(); + Ok(timestamps) +} + +/// Gets a list of all active wireguard interfaces on this device +pub fn get_list_of_wireguard_interfaces() -> Result, Error> { + let output = run_command("wg", &["show", "interfaces"])?; + let out = String::from_utf8(output.stdout)?; + let mut interfaces = Vec::new(); + for interface in out.split_ascii_whitespace() { + interfaces.push(interface.to_string()) } + Ok(interfaces) } #[test] @@ -162,63 +168,13 @@ fn test_durations() { #[test] fn test_get_wg_exit_clients_online() { - use crate::KI; - - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - let mut counter = 0; - - let link_args = &["show", "wg_exit", "latest-handshakes"]; - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "wg"); - counter += 1; - - match counter { - 1 => { - assert_eq!(args, link_args); - Ok(Output{ - stdout: format!("88gbNAZx7NoNK9hatYuDkeZOjQ8EBmJ8VBpcFhXPqHs= {}\nW1BwNSC9ulTutCg53KIlo+z2ihkXao3sXHaBBpaCXEw= 1536936247\n9jRr6euMHu3tBIsZyqxUmjbuKVVFZCBOYApOR2pLNkQ= 0", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()).as_bytes().to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("command called too many times"), - } - })); + let stdout = format!("88gbNAZx7NoNK9hatYuDkeZOjQ8EBmJ8VBpcFhXPqHs= {}\nW1BwNSC9ulTutCg53KIlo+z2ihkXao3sXHaBBpaCXEw= 1536936247\n9jRr6euMHu3tBIsZyqxUmjbuKVVFZCBOYApOR2pLNkQ= 0", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()); - assert_eq!(KI.get_wg_exit_clients_online("wg_exit").unwrap(), 1); + assert_eq!(get_wg_exit_clients_online_internal(stdout).unwrap(), 1); } #[test] fn test_get_last_handshake_time() { - use crate::KI; - - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - let mut counter = 0; - - let link_args = &["show", "wg1", "latest-handshakes"]; - KI.set_mock(Box::new(move |program, args| { - assert_eq!(program, "wg"); - counter += 1; - - match counter { - 1 => { - assert_eq!(args, link_args); - Ok(Output{ - stdout: format!("88gbNAZx7NoNK9hatYuDkeZOjQ8EBmJ8VBpcFhXPqHs= {}\nbGkj7Z6bX1593G0pExfzxocWKhS3Un9uifIhZP9c5iM= 1536936247\n9jRr6euMHu3tBIsZyqxUmjbuKVVFZCBOYApOR2pLNkQ= 0", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()).as_bytes().to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("command called too many times"), - } - })); - let wgkey1: WgKey = "88gbNAZx7NoNK9hatYuDkeZOjQ8EBmJ8VBpcFhXPqHs=" .parse() .unwrap(); @@ -228,9 +184,9 @@ fn test_get_last_handshake_time() { let wgkey3: WgKey = "9jRr6euMHu3tBIsZyqxUmjbuKVVFZCBOYApOR2pLNkQ=" .parse() .unwrap(); + let stdout = format!("88gbNAZx7NoNK9hatYuDkeZOjQ8EBmJ8VBpcFhXPqHs= {}\nbGkj7Z6bX1593G0pExfzxocWKhS3Un9uifIhZP9c5iM= 1536936247\n9jRr6euMHu3tBIsZyqxUmjbuKVVFZCBOYApOR2pLNkQ= 0", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()); - let res = KI - .get_last_handshake_time("wg1") + let res = get_last_active_handshake_time_internal(stdout.to_string()) .expect("Failed to run get_last_handshake_time!"); assert!(res.contains(&(wgkey3, SystemTime::UNIX_EPOCH))); assert!(res.contains(&( diff --git a/althea_kernel_interface/src/time.rs b/althea_kernel_interface/src/time.rs index ff41fd8a3..470ff5b28 100644 --- a/althea_kernel_interface/src/time.rs +++ b/althea_kernel_interface/src/time.rs @@ -1,25 +1,21 @@ -use std::time::{SystemTime, UNIX_EPOCH}; - -use super::KernelInterface; -use crate::KernelInterfaceError as Error; +use crate::{run_command, KernelInterfaceError as Error}; use std::process::Output; +use std::time::{SystemTime, UNIX_EPOCH}; -impl dyn KernelInterface { - /// Set the router's time using "date -s @seconds_since_unix_epoch" - pub fn set_local_time(&self, time: SystemTime) -> Result { - let time_secs = time.duration_since(UNIX_EPOCH).unwrap().as_secs(); - let t = format!("@{time_secs}"); - trace!("setting local time via 'date -s {}'", t); - match self.run_command("date", &["-s", t.as_str()]) { - Ok(output) => { - trace!("time was changed. New time is: {:?}", SystemTime::now()); - trace!("output: {:?}", output); - Ok(output) - } - Err(e) => { - error!("Failed to set local time: {:?}", e); - Err(e) - } +/// Set the router's time using "date -s @seconds_since_unix_epoch" +pub fn set_local_time(time: SystemTime) -> Result { + let time_secs = time.duration_since(UNIX_EPOCH).unwrap().as_secs(); + let t = format!("@{time_secs}"); + trace!("setting local time via 'date -s {}'", t); + match run_command("date", &["-s", t.as_str()]) { + Ok(output) => { + trace!("time was changed. New time is: {:?}", SystemTime::now()); + trace!("output: {:?}", output); + Ok(output) + } + Err(e) => { + error!("Failed to set local time: {:?}", e); + Err(e) } } } diff --git a/althea_kernel_interface/src/traffic_control.rs b/althea_kernel_interface/src/traffic_control.rs index 6adec46ee..c07d99f8d 100644 --- a/althea_kernel_interface/src/traffic_control.rs +++ b/althea_kernel_interface/src/traffic_control.rs @@ -4,456 +4,444 @@ //! Rita common in contrast is a simple limitation of the neighbors tunnel, which does not do //! any classful categorization. As a result one uses tbf and the other uses the clsssful htb +use crate::{run_command, KernelInterfaceError as Error}; use ipnetwork::IpNetwork; +use std::net::Ipv4Addr; -use crate::KernelInterface; -use crate::KernelInterfaceError as Error; +/// Determines if the provided interface has a configured qdisc +pub fn has_qdisc(iface_name: &str) -> Result { + let result = run_command("tc", &["qdisc", "show", "dev", iface_name])?; -use std::net::Ipv4Addr; + if !result.status.success() { + let res = String::from_utf8(result.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to check qdisc for {iface_name}! {res:?}" + ))); + } -impl dyn KernelInterface { - /// Determines if the provided interface has a configured qdisc - pub fn has_qdisc(&self, iface_name: &str) -> Result { - let result = self.run_command("tc", &["qdisc", "show", "dev", iface_name])?; + let stdout = &String::from_utf8(result.stdout)?; - if !result.status.success() { - let res = String::from_utf8(result.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to check qdisc for {iface_name}! {res:?}" - ))); - } + trace!("has_qdisc: {} {}", stdout, !stdout.contains("noqueue")); + Ok(!stdout.contains("noqueue")) +} - let stdout = &String::from_utf8(result.stdout)?; +/// Determines if the provided flow is assigned +pub fn has_flow(ip: Ipv4Addr, iface_name: &str) -> Result { + let class_id = get_class_id(ip); + let result = run_command("tc", &["filter", "show", "dev", iface_name])?; - trace!("has_qdisc: {} {}", stdout, !stdout.contains("noqueue")); - Ok(!stdout.contains("noqueue")) + if !result.status.success() { + let res = String::from_utf8(result.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to check filter for {class_id}! {res:?}" + ))); } - /// Determines if the provided flow is assigned - pub fn has_flow(&self, ip: Ipv4Addr, iface_name: &str) -> Result { - let class_id = self.get_class_id(ip); - let result = self.run_command("tc", &["filter", "show", "dev", iface_name])?; + let stdout = &String::from_utf8(result.stdout)?; + Ok(stdout.contains(&format!("1:{class_id}"))) +} - if !result.status.success() { - let res = String::from_utf8(result.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to check filter for {class_id}! {res:?}" - ))); - } +/// Gets the full flows list to pass to bulk functions +pub fn get_flows(iface_name: &str) -> Result { + let result = run_command("tc", &["filter", "show", "dev", iface_name])?; - let stdout = &String::from_utf8(result.stdout)?; - Ok(stdout.contains(&format!("1:{class_id}"))) + if !result.status.success() { + let res = String::from_utf8(result.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to get flows {res:?}" + ))); } + Ok(String::from_utf8(result.stdout)?) +} - /// Gets the full flows list to pass to bulk functions - pub fn get_flows(&self, iface_name: &str) -> Result { - let result = self.run_command("tc", &["filter", "show", "dev", iface_name])?; +/// A version of the flows check designed to be run from the raw input, more efficient +/// in the exit setup loop than running the same command several hundred times +pub fn has_flow_bulk(ip: Ipv4Addr, tc_out: &str) -> bool { + let class_id = get_class_id(ip); + tc_out.contains(&format!("1:{class_id}")) +} - if !result.status.success() { - let res = String::from_utf8(result.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to get flows {res:?}" - ))); - } - Ok(String::from_utf8(result.stdout)?) - } +/// Determines if the provided flow is assigned +pub fn has_class(ip: Ipv4Addr, iface_name: &str) -> Result { + let class_id = get_class_id(ip); + let result = run_command("tc", &["class", "show", "dev", iface_name])?; - /// A version of the flows check designed to be run from the raw input, more efficient - /// in the exit setup loop than running the same command several hundred times - pub fn has_flow_bulk(&self, ip: Ipv4Addr, tc_out: &str) -> bool { - let class_id = self.get_class_id(ip); - tc_out.contains(&format!("1:{class_id}")) + if !result.status.success() { + let res = String::from_utf8(result.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to check filter for {class_id}! {res:?}" + ))); } - /// Determines if the provided flow is assigned - pub fn has_class(&self, ip: Ipv4Addr, iface_name: &str) -> Result { - let class_id = self.get_class_id(ip); - let result = self.run_command("tc", &["class", "show", "dev", iface_name])?; - - if !result.status.success() { - let res = String::from_utf8(result.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to check filter for {class_id}! {res:?}" - ))); - } - - let stdout = &String::from_utf8(result.stdout)?; - for line in stdout.lines() { - let split = line.split_ascii_whitespace(); - for item in split { - if *item == format!("1:{class_id}") { - return Ok(true); - } + let stdout = &String::from_utf8(result.stdout)?; + for line in stdout.lines() { + let split = line.split_ascii_whitespace(); + for item in split { + if *item == format!("1:{class_id}") { + return Ok(true); } } - Ok(false) } + Ok(false) +} - /// Determines if the provided interface has a configured qdisc - pub fn has_limit(&self, iface_name: &str) -> Result { - let result = self.run_command("tc", &["qdisc", "show", "dev", iface_name])?; - - if !result.status.success() { - let res = String::from_utf8(result.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to check limit for {iface_name}! {res:?}" - ))); - } +/// Determines if the provided interface has a configured qdisc +pub fn has_limit(iface_name: &str) -> Result { + let result = run_command("tc", &["qdisc", "show", "dev", iface_name])?; - let stdout = &String::from_utf8(result.stdout)?; - Ok((stdout.contains("htb") || stdout.contains("tbf")) - && !stdout.contains("codel") - && !stdout.contains("cake") - && !stdout.contains("noqueue")) + if !result.status.success() { + let res = String::from_utf8(result.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to check limit for {iface_name}! {res:?}" + ))); } - /// Determines if the provided interface has a configured qdisc - pub fn has_cake(&self, iface_name: &str) -> Result { - let result = self.run_command("tc", &["qdisc", "show", "dev", iface_name])?; + let stdout = &String::from_utf8(result.stdout)?; + Ok((stdout.contains("htb") || stdout.contains("tbf")) + && !stdout.contains("codel") + && !stdout.contains("cake") + && !stdout.contains("noqueue")) +} - if !result.status.success() { - let res = String::from_utf8(result.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to check limit for {iface_name}! {res:?}" - ))); - } +/// Determines if the provided interface has a configured qdisc +pub fn has_cake(iface_name: &str) -> Result { + let result = run_command("tc", &["qdisc", "show", "dev", iface_name])?; - let stdout = &String::from_utf8(result.stdout)?; - Ok((stdout.contains("codel") || stdout.contains("cake")) - && !stdout.contains("tbf") - && !stdout.contains("noqueue") - && !stdout.contains("htb")) + if !result.status.success() { + let res = String::from_utf8(result.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to check limit for {iface_name}! {res:?}" + ))); } - /// This sets up latency protecting flow control, either cake on openwrt - /// or fq_codel on older devices/kernels, the Cake configuration sets several advanced parameters - /// that are not reflected if we fall back to codel - pub fn set_codel_shaping(&self, iface_name: &str, speed: Option) -> Result<(), Error> { - let operator = if self.has_qdisc(iface_name)? { - "change" - } else { - "add" - }; - let mut cake_args = vec![ - "qdisc", operator, "dev", iface_name, "root", "handle", "1:", "cake", - ]; - // declared here but used only in the match to provide a longer lifetime - let mbit; - - match speed { - Some(val) => { - mbit = format!("{val}mbit"); - cake_args.extend(["bandwidth", &mbit]) - } - None => cake_args.extend(["unlimited"]), - } + let stdout = &String::from_utf8(result.stdout)?; + Ok((stdout.contains("codel") || stdout.contains("cake")) + && !stdout.contains("tbf") + && !stdout.contains("noqueue") + && !stdout.contains("htb")) +} - // cake arguments for per hop tunnels only - cake_args.extend([ - // we want to use the 'internet' parameter here because the total rtt - // of the path from endpoint to endpoint is what this value cares about - // not neighbor to neighbor - "internet", - // diffserv4 allocates 50% of the connection to video streams and - // generally recognizes more traffic classes than the default diffserv3 - // there's some debate by cake maintainers internally if this is a good idea - "diffserv4", - ]); +/// This sets up latency protecting flow control, either cake on openwrt +/// or fq_codel on older devices/kernels, the Cake configuration sets several advanced parameters +/// that are not reflected if we fall back to codel +pub fn set_codel_shaping(iface_name: &str, speed: Option) -> Result<(), Error> { + let operator = if has_qdisc(iface_name)? { + "change" + } else { + "add" + }; + let mut cake_args = vec![ + "qdisc", operator, "dev", iface_name, "root", "handle", "1:", "cake", + ]; + // declared here but used only in the match to provide a longer lifetime + let mbit; + + match speed { + Some(val) => { + mbit = format!("{val}mbit"); + cake_args.extend(["bandwidth", &mbit]) + } + None => cake_args.extend(["unlimited"]), + } - let output = self.run_command("tc", &cake_args)?; + // cake arguments for per hop tunnels only + cake_args.extend([ + // we want to use the 'internet' parameter here because the total rtt + // of the path from endpoint to endpoint is what this value cares about + // not neighbor to neighbor + "internet", + // diffserv4 allocates 50% of the connection to video streams and + // generally recognizes more traffic classes than the default diffserv3 + // there's some debate by cake maintainers internally if this is a good idea + "diffserv4", + ]); + + let output = run_command("tc", &cake_args)?; + + if !output.status.success() { + trace!( + "No support for the Cake qdisc is detected, falling back to fq_codel. Error: {}", + String::from_utf8(output.stderr)? + ); + trace!("Command was tc {}", crate::print_str_array(&cake_args)); + warn!("sch_cake is strongly recommended, you should install it"); + let output = run_command( + "tc", + &[ + "qdisc", operator, "dev", iface_name, "root", "handle", "1:", "fq_codel", "target", + "100ms", + ], + )?; if !output.status.success() { + // final fallback on FIFO this is a very old machine indeed trace!( - "No support for the Cake qdisc is detected, falling back to fq_codel. Error: {}", + "No support for the fq codel qdisc is detected, falling back to fifo. Error: {}", String::from_utf8(output.stderr)? ); trace!("Command was tc {}", crate::print_str_array(&cake_args)); - warn!("sch_cake is strongly recommended, you should install it"); - let output = self.run_command( + let output = run_command( "tc", &[ - "qdisc", operator, "dev", iface_name, "root", "handle", "1:", "fq_codel", - "target", "100ms", + "qdisc", operator, "dev", iface_name, "root", "handle", "1:", "fifo", ], )?; - if !output.status.success() { - // final fallback on FIFO this is a very old machine indeed - trace!( - "No support for the fq codel qdisc is detected, falling back to fifo. Error: {}", - String::from_utf8(output.stderr)? - ); - trace!("Command was tc {}", crate::print_str_array(&cake_args)); - let output = self.run_command( - "tc", - &[ - "qdisc", operator, "dev", iface_name, "root", "handle", "1:", "fifo", - ], - )?; - if !output.status.success() { - let res = String::from_utf8(output.stderr)?; - error!("Cake, fq_codel and fallback FIFO have all failed!"); - return Err(Error::TrafficControlError(format!( - "Failed to create new qdisc limit! {res:?}" - ))); - } + let res = String::from_utf8(output.stderr)?; + error!("Cake, fq_codel and fallback FIFO have all failed!"); + return Err(Error::TrafficControlError(format!( + "Failed to create new qdisc limit! {res:?}" + ))); } } - - Ok(()) } - /// Creates a qdisc limit with the given bandwidth tuned for the correct rate - /// this limit uses tbf which is classless and faster since we leave prioritization - /// to the fq_codel on the ingress and egress interfaces - pub fn set_classless_limit(&self, iface_name: &str, bw: u32) -> Result<(), Error> { - if self.has_qdisc(iface_name)? { - self.delete_qdisc(iface_name)?; - } - - // we need 1kbyte of burst cache per mbit of bandwidth to actually keep things - // moving - let burst = bw * 1000u32; - // amount of time a packet can spend in the burst cache, 40ms - let latency = 40u32; - - let output = self.run_command( - "tc", - &[ - "qdisc", - "add", - "dev", - iface_name, - "root", - "handle", - "1:", - "tbf", - "latency", - &format!("{latency}ms"), - "burst", - &format!("{burst}"), - "rate", - &format!("{bw}kbit"), - ], - )?; + Ok(()) +} - if output.status.success() { - Ok(()) - } else { - let res = String::from_utf8(output.stderr)?; - Err(Error::TrafficControlError(format!( - "Failed to create new qdisc limit! {res:?}" - ))) - } +/// Creates a qdisc limit with the given bandwidth tuned for the correct rate +/// this limit uses tbf which is classless and faster since we leave prioritization +/// to the fq_codel on the ingress and egress interfaces +pub fn set_classless_limit(iface_name: &str, bw: u32) -> Result<(), Error> { + if has_qdisc(iface_name)? { + delete_qdisc(iface_name)?; } - /// Creates the root limit on the wg_exit tunnel for the exit, under which all other classes - /// operate - pub fn create_root_classful_limit(&self, iface_name: &str) -> Result<(), Error> { - let output = self.run_command( - "tc", - &[ - "qdisc", - "add", - "dev", - iface_name, - "root", - "handle", - "1:", - "htb", - "default", - "0", - "r2q", - "1", - "direct_qlen", - "1000000", - ], - )?; + // we need 1kbyte of burst cache per mbit of bandwidth to actually keep things + // moving + let burst = bw * 1000u32; + // amount of time a packet can spend in the burst cache, 40ms + let latency = 40u32; + + let output = run_command( + "tc", + &[ + "qdisc", + "add", + "dev", + iface_name, + "root", + "handle", + "1:", + "tbf", + "latency", + &format!("{latency}ms"), + "burst", + &format!("{burst}"), + "rate", + &format!("{bw}kbit"), + ], + )?; + + if output.status.success() { + Ok(()) + } else { + let res = String::from_utf8(output.stderr)?; + Err(Error::TrafficControlError(format!( + "Failed to create new qdisc limit! {res:?}" + ))) + } +} - if output.status.success() { - Ok(()) - } else { - let res = String::from_utf8(output.stderr)?; - Err(Error::TrafficControlError(format!( - "Failed to create new qdisc limit! {res:?}" - ))) - } +/// Creates the root limit on the wg_exit tunnel for the exit, under which all other classes +/// operate +pub fn create_root_classful_limit(iface_name: &str) -> Result<(), Error> { + let output = run_command( + "tc", + &[ + "qdisc", + "add", + "dev", + iface_name, + "root", + "handle", + "1:", + "htb", + "default", + "0", + "r2q", + "1", + "direct_qlen", + "1000000", + ], + )?; + + if output.status.success() { + Ok(()) + } else { + let res = String::from_utf8(output.stderr)?; + Err(Error::TrafficControlError(format!( + "Failed to create new qdisc limit! {res:?}" + ))) } +} - pub fn delete_class(&self, iface_name: &str, ip: Ipv4Addr) -> Result<(), Error> { - let class_id = self.get_class_id(ip); - match self.has_class(ip, iface_name) { - Ok(has_class) => { - if has_class { - self.run_command( - "tc", - &[ - "class", - "del", - "dev", - iface_name, - "parent", - "1:", - "classid", - &format!("1:{class_id}"), - ], - )?; - } - Ok(()) - } - Err(e) => { - error!( - "Unable to find class for ip {:?} on {:?} with {:?}", - ip, iface_name, e - ); - Err(e) +pub fn delete_class(iface_name: &str, ip: Ipv4Addr) -> Result<(), Error> { + let class_id = get_class_id(ip); + match has_class(ip, iface_name) { + Ok(has_class) => { + if has_class { + run_command( + "tc", + &[ + "class", + "del", + "dev", + iface_name, + "parent", + "1:", + "classid", + &format!("1:{class_id}"), + ], + )?; } + Ok(()) } - } - - pub fn set_class_limit( - &self, - iface_name: &str, - min_bw: u32, - max_bw: u32, - ip: Ipv4Addr, - ) -> Result<(), Error> { - let class_id = self.get_class_id(ip); - let modifier = if self.has_class(ip, iface_name)? { - "change" - } else { - "add" - }; - - let output = self.run_command( - "tc", - &[ - "class", - modifier, - "dev", - iface_name, - "parent", - "1:", - "classid", - &format!("1:{class_id}"), - "htb", - "rate", - &format!("{min_bw}kbit"), - "ceil", - &format!("{max_bw}kbit"), - ], - )?; - - if !output.status.success() { - let res = String::from_utf8(output.stderr)?; - return Err(Error::TrafficControlError(format!( - "Failed to update qdisc class limit! {res:?}" - ))); + Err(e) => { + error!( + "Unable to find class for ip {:?} on {:?} with {:?}", + ip, iface_name, e + ); + Err(e) } - - Ok(()) } +} - /// Generates a unique traffic class id for a exit user, essentially a really dumb hashing function - pub fn get_class_id(&self, ip: Ipv4Addr) -> u32 { - let num: u32 = ip.into(); - num % 9999 //9999 is the maximum flow id value allowed +pub fn set_class_limit( + iface_name: &str, + min_bw: u32, + max_bw: u32, + ip: Ipv4Addr, +) -> Result<(), Error> { + let class_id = get_class_id(ip); + let modifier = if has_class(ip, iface_name)? { + "change" + } else { + "add" + }; + + let output = run_command( + "tc", + &[ + "class", + modifier, + "dev", + iface_name, + "parent", + "1:", + "classid", + &format!("1:{class_id}"), + "htb", + "rate", + &format!("{min_bw}kbit"), + "ceil", + &format!("{max_bw}kbit"), + ], + )?; + + if !output.status.success() { + let res = String::from_utf8(output.stderr)?; + return Err(Error::TrafficControlError(format!( + "Failed to update qdisc class limit! {res:?}" + ))); } - /// Filters traffic from a given ipv6 address into the class that we are using - /// to shape that traffic on the exit side, uses the last two octets of the ip - /// to generate a class id. - pub fn create_flow_by_ipv6( - &self, - iface_name: &str, - ip_net: IpNetwork, - ip: Ipv4Addr, - ) -> Result<(), Error> { - let class_id = self.get_class_id(ip); - - let output = self.run_command( - "tc", - &[ - "filter", - "add", - "dev", - iface_name, - "parent", - "1:", - "protocol", - "ipv6", - "u32", - "match", - "ip6", - "dst", - &ip_net.to_string(), - "flowid", - &format!("1:{class_id}"), - ], - )?; - - if output.status.success() { - Ok(()) - } else { - let res = String::from_utf8(output.stderr)?; - Err(Error::TrafficControlError(format!( - "Failed to create limit by ip! {res:?}" - ))) - } - } + Ok(()) +} - /// Filters traffic from a given ipv4 address into the class that we are using - /// to shape that traffic on the exit side, uses the last two octets of the ip - /// to generate a class id. - pub fn create_flow_by_ip(&self, iface_name: &str, ip: Ipv4Addr) -> Result<(), Error> { - let class_id = self.get_class_id(ip); +/// Generates a unique traffic class id for a exit user, essentially a really dumb hashing function +pub fn get_class_id(ip: Ipv4Addr) -> u32 { + let num: u32 = ip.into(); + num % 9999 //9999 is the maximum flow id value allowed +} - let output = self.run_command( - "tc", - &[ - "filter", - "add", - "dev", - iface_name, - "parent", - "1:", - "protocol", - "ip", - "u32", - "match", - "ip", - "dst", - &format!("{ip}/32"), - "flowid", - &format!("1:{class_id}"), - ], - )?; +/// Filters traffic from a given ipv6 address into the class that we are using +/// to shape that traffic on the exit side, uses the last two octets of the ip +/// to generate a class id. +pub fn create_flow_by_ipv6(iface_name: &str, ip_net: IpNetwork, ip: Ipv4Addr) -> Result<(), Error> { + let class_id = get_class_id(ip); + + let output = run_command( + "tc", + &[ + "filter", + "add", + "dev", + iface_name, + "parent", + "1:", + "protocol", + "ipv6", + "u32", + "match", + "ip6", + "dst", + &ip_net.to_string(), + "flowid", + &format!("1:{class_id}"), + ], + )?; + + if output.status.success() { + Ok(()) + } else { + let res = String::from_utf8(output.stderr)?; + Err(Error::TrafficControlError(format!( + "Failed to create limit by ip! {res:?}" + ))) + } +} - if output.status.success() { - Ok(()) - } else { - let res = String::from_utf8(output.stderr)?; - Err(Error::TrafficControlError(format!( - "Failed to create limit by ip! {res:?}" - ))) - } +/// Filters traffic from a given ipv4 address into the class that we are using +/// to shape that traffic on the exit side, uses the last two octets of the ip +/// to generate a class id. +pub fn create_flow_by_ip(iface_name: &str, ip: Ipv4Addr) -> Result<(), Error> { + let class_id = get_class_id(ip); + + let output = run_command( + "tc", + &[ + "filter", + "add", + "dev", + iface_name, + "parent", + "1:", + "protocol", + "ip", + "u32", + "match", + "ip", + "dst", + &format!("{ip}/32"), + "flowid", + &format!("1:{class_id}"), + ], + )?; + + if output.status.success() { + Ok(()) + } else { + let res = String::from_utf8(output.stderr)?; + Err(Error::TrafficControlError(format!( + "Failed to create limit by ip! {res:?}" + ))) } +} - /// deletes the interface qdisc - pub fn delete_qdisc(&self, iface_name: &str) -> Result<(), Error> { - let output = self.run_command("tc", &["qdisc", "del", "dev", iface_name, "root"])?; - if output.status.success() { - Ok(()) - } else { - Err(Error::TrafficControlError( - "Failed to delete qdisc limit".to_string(), - )) - } +/// deletes the interface qdisc +pub fn delete_qdisc(iface_name: &str) -> Result<(), Error> { + let output = run_command("tc", &["qdisc", "del", "dev", iface_name, "root"])?; + if output.status.success() { + Ok(()) + } else { + Err(Error::TrafficControlError( + "Failed to delete qdisc limit".to_string(), + )) } } #[test] fn get_id() { - use crate::KI; - println!("{}", KI.get_class_id("172.168.4.121".parse().unwrap())); + println!("{}", get_class_id("172.168.4.121".parse().unwrap())); } diff --git a/althea_kernel_interface/src/udp_socket_table.rs b/althea_kernel_interface/src/udp_socket_table.rs index 1253398e8..9b47d68ce 100644 --- a/althea_kernel_interface/src/udp_socket_table.rs +++ b/althea_kernel_interface/src/udp_socket_table.rs @@ -1,4 +1,3 @@ -use crate::KernelInterface; use crate::KernelInterfaceError; use crate::KernelInterfaceError as Error; use std::collections::HashSet; @@ -29,31 +28,29 @@ fn parse_local_port(s: &str) -> Result { } } -impl dyn KernelInterface { - fn read_udp_socket_table(&self) -> Result { - let mut f = File::open("/proc/net/udp")?; - let mut contents = String::new(); +fn read_udp_socket_table() -> Result { + let mut f = File::open("/proc/net/udp")?; + let mut contents = String::new(); - f.read_to_string(&mut contents)?; + f.read_to_string(&mut contents)?; - Ok(contents) - } + Ok(contents) +} - /// Returns list of ports in use as seen in the UDP socket table (/proc/net/udp) - pub fn used_ports(&self) -> Result, Error> { - let udp_sockets_table = self.read_udp_socket_table()?; - let mut lines = udp_sockets_table.split('\n'); +/// Returns list of ports in use as seen in the UDP socket table (/proc/net/udp) +pub fn used_ports() -> Result, Error> { + let udp_sockets_table = read_udp_socket_table()?; + let mut lines = udp_sockets_table.split('\n'); - lines.next(); // advance iterator to skip header + lines.next(); // advance iterator to skip header - let ports: HashSet = lines - .take_while(|line| !line.is_empty()) // until end of the table is reached, - .map(parse_local_port) // parse each udp port, - .filter_map(Result::ok) // only taking those which parsed successfully - .collect(); + let ports: HashSet = lines + .take_while(|line| !line.is_empty()) // until end of the table is reached, + .map(parse_local_port) // parse each udp port, + .filter_map(Result::ok) // only taking those which parsed successfully + .collect(); - Ok(ports) - } + Ok(ports) } #[test] diff --git a/althea_kernel_interface/src/upgrade.rs b/althea_kernel_interface/src/upgrade.rs index 3c56a260f..2cdf540af 100644 --- a/althea_kernel_interface/src/upgrade.rs +++ b/althea_kernel_interface/src/upgrade.rs @@ -1,78 +1,75 @@ -use super::KernelInterface; use crate::{ opkg_feeds::{get_release_feed, set_release_feed, CUSTOMFEEDS}, - KernelInterfaceError as Error, + run_command, KernelInterfaceError as Error, }; use althea_types::{OpkgCommand, SysupgradeCommand}; use std::process::Output; -impl dyn KernelInterface { - pub fn perform_sysupgrade(&self, command: SysupgradeCommand) -> Result { - //If empty url, return error - if command.url.is_empty() { - info!("Empty url given to sysupgrade"); - return Err(Error::RuntimeError( - "Empty url given to sysupgrade".to_string(), - )); - } - - // append path to end of flags - let mut args = if command.flags.is_some() { - command.flags.unwrap() - } else { - Vec::new() - }; - args.push(command.url); - let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); - info!( - "Running the command /sbin/sysupgrade with args: {:?}", - args_ref - ); - self.run_command("/sbin/sysupgrade", &args_ref) +pub fn perform_sysupgrade(command: SysupgradeCommand) -> Result { + //If empty url, return error + if command.url.is_empty() { + info!("Empty url given to sysupgrade"); + return Err(Error::RuntimeError( + "Empty url given to sysupgrade".to_string(), + )); } - /// This function checks if the function provided is update or install. In case of install, for each of the packages - /// present, the arguments given are applied and opkg install is run - pub fn perform_opkg(&self, command: OpkgCommand) -> Result { - match command { - OpkgCommand::Install { - packages, - arguments, - } => { - let mut args = arguments; - args.insert(0, "install".to_string()); - for package in packages { - args.push(package); - } - info!("Running opkg install with args: {:?}", args); - let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); - self.run_command("opkg", &args_ref) - } - OpkgCommand::Remove { - packages, - arguments, - } => { - let mut args = arguments; - args.insert(0, "remove".to_string()); - for package in packages { - args.push(package); - } - info!("Running opkg remove with args: {:?}", args); - let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); - self.run_command("opkg", &args_ref) + // append path to end of flags + let mut args = if command.flags.is_some() { + command.flags.unwrap() + } else { + Vec::new() + }; + args.push(command.url); + let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); + info!( + "Running the command /sbin/sysupgrade with args: {:?}", + args_ref + ); + run_command("/sbin/sysupgrade", &args_ref) +} + +/// This function checks if the function provided is update or install. In case of install, for each of the packages +/// present, the arguments given are applied and opkg install is run +pub fn perform_opkg(command: OpkgCommand) -> Result { + match command { + OpkgCommand::Install { + packages, + arguments, + } => { + let mut args = arguments; + args.insert(0, "install".to_string()); + for package in packages { + args.push(package); } - OpkgCommand::Update { - feed, - feed_name, - arguments, - } => { - handle_release_feed_update(feed, feed_name)?; - let mut args = arguments; - args.insert(0, "update".to_string()); - info!("Running opkg update with args: {:?}", args); - let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); - self.run_command("opkg", &args_ref) + info!("Running opkg install with args: {:?}", args); + let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); + run_command("opkg", &args_ref) + } + OpkgCommand::Remove { + packages, + arguments, + } => { + let mut args = arguments; + args.insert(0, "remove".to_string()); + for package in packages { + args.push(package); } + info!("Running opkg remove with args: {:?}", args); + let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); + run_command("opkg", &args_ref) + } + OpkgCommand::Update { + feed, + feed_name, + arguments, + } => { + handle_release_feed_update(feed, feed_name)?; + let mut args = arguments; + args.insert(0, "update".to_string()); + info!("Running opkg update with args: {:?}", args); + let args_ref: Vec<&str> = args.iter().map(std::ops::Deref::deref).collect(); + run_command("opkg", &args_ref) } } } diff --git a/althea_kernel_interface/src/wg_iface_counter.rs b/althea_kernel_interface/src/wg_iface_counter.rs index db1e2427f..4f1d55ddf 100644 --- a/althea_kernel_interface/src/wg_iface_counter.rs +++ b/althea_kernel_interface/src/wg_iface_counter.rs @@ -2,8 +2,8 @@ //! this is mostly used in client and exit billing where we only have to concern ourselves with //! a single destination and a single price. -use crate::KernelInterfaceError as Error; -use crate::{KernelInterface, KernelInterfaceError}; +use crate::KernelInterfaceError; +use crate::{run_command, KernelInterfaceError as Error}; use althea_types::WgKey; use regex::Regex; use std::collections::HashMap; @@ -43,75 +43,54 @@ pub fn prepare_usage_history( } } -impl dyn KernelInterface { - /// Takes a wg interface name and provides upload and download since creation in bytes - /// in a hashmap indexed by peer WireGuard key - pub fn read_wg_counters(&self, wg_name: &str) -> Result, Error> { - let output = self.run_command("wg", &["show", wg_name, "transfer"])?; - if !output.stderr.is_empty() { - return Err(KernelInterfaceError::RuntimeError(format!( - "received error from wg command: {}", - String::from_utf8(output.stderr)? - ))); - } - - lazy_static! { - static ref RE: Regex = Regex::new( - r"(?P[+/=0-9a-zA-Z]+)\t(?P[0-9]+)\t(?P[0-9]+)\n*" - ) - .expect("Unable to compile regular expression"); - } +/// Internal function to parse the output of `wg show transfer` +fn read_wg_counters_internal(stdout: String) -> Result, Error> { + lazy_static! { + static ref RE: Regex = + Regex::new(r"(?P[+/=0-9a-zA-Z]+)\t(?P[0-9]+)\t(?P[0-9]+)\n*") + .expect("Unable to compile regular expression"); + } - let mut result = HashMap::new(); - for item in RE.captures_iter(&String::from_utf8(output.stdout)?) { - let usage = WgUsage { - upload: item["upload"].parse()?, - download: item["download"].parse()?, - }; - match item["key"].parse() { - Ok(key) => { - result.insert(key, usage); - } - Err(e) => warn!( - "Failed to parse WgKey {} with {:?}", - item["key"].to_string(), - e - ), + let mut result = HashMap::new(); + for item in RE.captures_iter(&stdout) { + let usage = WgUsage { + upload: item["upload"].parse()?, + download: item["download"].parse()?, + }; + match item["key"].parse() { + Ok(key) => { + result.insert(key, usage); } + Err(e) => warn!( + "Failed to parse WgKey {} with {:?}", + item["key"].to_string(), + e + ), } + } + + Ok(result) +} - Ok(result) +/// Takes a wg interface name and provides upload and download since creation in bytes +/// in a hashmap indexed by peer WireGuard key +pub fn read_wg_counters(wg_name: &str) -> Result, Error> { + let output = run_command("wg", &["show", wg_name, "transfer"])?; + if !output.stderr.is_empty() { + return Err(KernelInterfaceError::RuntimeError(format!( + "received error from wg command: {}", + String::from_utf8(output.stderr)? + ))); } + read_wg_counters_internal(String::from_utf8(output.stdout)?) } #[test] fn test_read_wg_counters() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - assert_eq!(program, "wg"); - assert_eq!(args, vec!["show", "wg_exit", "transfer"]); - Ok(Output { - stdout: b"jkIodvXKgij/rAEQXFEPJpls6ooxXJEC5XlWA1uUPUg=\t821519724\t13592616000" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - })); - let wg_counter = KI - .read_wg_counters("wg_exit") - .expect("Unable to parse wg counters"); + let wg_counter = read_wg_counters_internal( + "jkIodvXKgij/rAEQXFEPJpls6ooxXJEC5XlWA1uUPUg=\t821519724\t13592616000".to_string(), + ) + .unwrap(); let test_key = "jkIodvXKgij/rAEQXFEPJpls6ooxXJEC5XlWA1uUPUg=" .parse() .unwrap(); @@ -124,43 +103,19 @@ fn test_read_wg_counters() { #[test] fn test_read_wg_exit_counters() { - use crate::KI; - use std::os::unix::process::ExitStatusExt; - use std::process::ExitStatus; - use std::process::Output; - - let mut counter = 0; - - KI.set_mock(Box::new(move |program, args| { - counter += 1; - match counter { - 1 => { - assert_eq!(program, "wg"); - assert_eq!(args, vec!["show", "wg_exit", "transfer"]); - Ok(Output { - stdout: b"7fYutmH8iHIcuKjnzcgaDBNpVRw8ly0XMYFr7PtirDI=\t0\t0 -fFGhz1faSAqNjTqT5rpBWLD/FLrP6P/P59Z2Eo3jQDo=\t0\t3318456 -oum4Nd5nngTjG5Hw+XFoLk18pTY8DA7bl2OIwWkc4wQ=\t0\t0 -TFG8LAio7MDd+i5tExNX/vxR1pqpgNqo+RiUkmBekmU=\t0\t0 -Iz668/X70eo/PF9C94cKAZjrSjU961V8xndxTtk0FRM=\t7088439728\t15281630160 -Hgu1A3JFol3D6TFsmnjX/PVvupl0W2wMee0wRVFb0Aw=\t122193456\t1120342792 -7dxulyk1UcCJ0zUDcGbV+CQRY0uGUnbY5exi6I8EeyE=\t351530232\t5424629680 -b6HGtuWLAIHyINOgL7euzrMsMfzHIie5kYDScSCT7Ds=\t67526804\t88741160 -RW1XPRn4nJQaDqqeDFRilPjtYOUBitXIuHwoKZAtKWw=\t480230868\t3531367092 -AJYeYn4R+I+Jc7xiKQ15ImruYFXybTiR6BB6Ip3/njs=\t1777907382\t2034640104 -jL+LlqHAM63Qd9/ynAuqqn4wrYO7Hp8cYMlnf2OoSH8=\t679969972\t6539417596 -" - .to_vec(), - stderr: b"".to_vec(), - status: ExitStatus::from_raw(0), - }) - } - _ => panic!("Unexpected call {} {:?} {:?}", counter, program, args), - } - })); - let wg_counter = KI - .read_wg_counters("wg_exit") - .expect("Unable to parse wg counters"); + let stdout = "7fYutmH8iHIcuKjnzcgaDBNpVRw8ly0XMYFr7PtirDI=\t0\t0 + fFGhz1faSAqNjTqT5rpBWLD/FLrP6P/P59Z2Eo3jQDo=\t0\t3318456 + oum4Nd5nngTjG5Hw+XFoLk18pTY8DA7bl2OIwWkc4wQ=\t0\t0 + TFG8LAio7MDd+i5tExNX/vxR1pqpgNqo+RiUkmBekmU=\t0\t0 + Iz668/X70eo/PF9C94cKAZjrSjU961V8xndxTtk0FRM=\t7088439728\t15281630160 + Hgu1A3JFol3D6TFsmnjX/PVvupl0W2wMee0wRVFb0Aw=\t122193456\t1120342792 + 7dxulyk1UcCJ0zUDcGbV+CQRY0uGUnbY5exi6I8EeyE=\t351530232\t5424629680 + b6HGtuWLAIHyINOgL7euzrMsMfzHIie5kYDScSCT7Ds=\t67526804\t88741160 + RW1XPRn4nJQaDqqeDFRilPjtYOUBitXIuHwoKZAtKWw=\t480230868\t3531367092 + AJYeYn4R+I+Jc7xiKQ15ImruYFXybTiR6BB6Ip3/njs=\t1777907382\t2034640104 + jL+LlqHAM63Qd9/ynAuqqn4wrYO7Hp8cYMlnf2OoSH8=\t679969972\t6539417596 + "; + let wg_counter = read_wg_counters_internal(stdout.to_string()).unwrap(); let test_key = "Iz668/X70eo/PF9C94cKAZjrSjU961V8xndxTtk0FRM=" .parse() diff --git a/antenna_forwarding_client/Cargo.toml b/antenna_forwarding_client/Cargo.toml index 8322ef2ac..393c49a97 100644 --- a/antenna_forwarding_client/Cargo.toml +++ b/antenna_forwarding_client/Cargo.toml @@ -15,5 +15,4 @@ antenna_forwarding_protocol = {path = "../antenna_forwarding_protocol"} serde_json = "1.0" serde = "1.0" get_if_addrs = "0.5" -lazy_static = "1.5" rand = "0.8" diff --git a/antenna_forwarding_client/src/lib.rs b/antenna_forwarding_client/src/lib.rs index 7154426f8..3fc61636e 100644 --- a/antenna_forwarding_client/src/lib.rs +++ b/antenna_forwarding_client/src/lib.rs @@ -4,11 +4,9 @@ #[macro_use] extern crate log; -#[macro_use] -extern crate lazy_static; -use althea_kernel_interface::KernelInterface; -use althea_kernel_interface::LinuxCommandRunner; +use althea_kernel_interface::interface_tools::get_ip_from_iface; +use althea_kernel_interface::run_command; use althea_types::Identity; use althea_types::WgKey; use antenna_forwarding_protocol::process_streams; @@ -34,10 +32,6 @@ use std::time::Instant; mod error; pub use error::AntennaForwardingError; -lazy_static! { - pub static ref KI: Box = Box::new(LinuxCommandRunner {}); -} - const SLEEP_TIME: Duration = Duration::from_secs(20); /// The timeout time for pinging a local antenna, 100ms is very /// very generous here as they should all respond really within 5ms @@ -312,7 +306,7 @@ fn find_antenna( trace!("Trying interface {}, with test ip {}", iface, our_ip); // this acts as a wildcard deletion across all interfaces, which is frankly really // dangerous if our default route overlaps, or if you enter an exit route ip - let _ = KI.run_command("ip", &["route", "del", &format!("{target_ip}/32")]); + let _ = run_command("ip", &["route", "del", &format!("{target_ip}/32")]); for iface in interfaces { // cleans up all previous forwarding ip's in some way this is more dangerous than the previous // solution, which only cleaned up the target and destination ip's. But the more through cleanup @@ -322,7 +316,7 @@ fn find_antenna( // not cause issues with failing the find antenna command cleanup_interface(iface)?; } - let res = KI.run_command( + let res = run_command( "ip", &["addr", "add", &format!("{our_ip}/32"), "dev", iface], ); @@ -330,7 +324,7 @@ fn find_antenna( // you need to use src here to disambiguate the sending address // otherwise the first available ipv4 address on the interface will // be used - match KI.run_command( + match run_command( "ip", &[ "route", @@ -433,12 +427,12 @@ fn send_error_message(server_stream: &mut TcpStream, message: String) { } fn cleanup_interface(iface: &str) -> Result<(), AntennaForwardingError> { - let values = KI.get_ip_from_iface(iface)?; + let values = get_ip_from_iface(iface)?; for (ip, netmask) in values { // we only clean up very specific routes, this doesn't prevent us from causing problems // but it does help prevent us from doing things like removing the default route. if netmask == 32 { - let _ = KI.run_command("ip", &["addr", "del", &format!("{ip}/32"), "dev", iface]); + let _ = run_command("ip", &["addr", "del", &format!("{ip}/32"), "dev", iface]); } } Ok(()) diff --git a/clu/src/lib.rs b/clu/src/lib.rs index 46619371e..cd57cd255 100644 --- a/clu/src/lib.rs +++ b/clu/src/lib.rs @@ -3,7 +3,9 @@ extern crate log; #[macro_use] extern crate lazy_static; -use althea_kernel_interface::KI; +use althea_kernel_interface::create_wg_key::{create_wg_key, create_wg_keypair}; +use althea_kernel_interface::interface_tools::{del_interface, get_interfaces}; +use althea_kernel_interface::ip_route::restore_default_route; use clarity::PrivateKey; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; @@ -72,7 +74,7 @@ pub fn cleanup() -> Result<(), NewCluError> { let mut interfaces: Vec = Vec::new(); - for i in KI.get_interfaces()? { + for i in get_interfaces()? { if RE.is_match(&i) { interfaces.push(i); } @@ -90,7 +92,7 @@ pub fn cleanup() -> Result<(), NewCluError> { /// are not deleted correctly with parallel deletion on some systems pub fn del_multiple_interfaces_sync(interfaces: Vec) -> Result<(), NewCluError> { for interface in interfaces { - KI.del_interface(&interface)?; + del_interface(&interface)?; } Ok(()) } @@ -119,7 +121,7 @@ pub fn del_multiple_interfaces(interfaces: Vec) { let mut threads = Vec::new(); // for lifetime reasons for name in batch { - threads.push(thread::spawn(move || KI.del_interface(&name.clone()))); + threads.push(thread::spawn(move || del_interface(&name.clone()))); } for thread in threads { // we want to wait for these to complete, we don't care that much if they fail @@ -133,7 +135,7 @@ fn linux_init(settings: RitaClientSettings) -> Result Result>(); for s in out { - KI.run_command("kill", &[s.trim()]).unwrap(); + run_command("kill", &[s.trim()]).unwrap(); } } fn get_current_exit(ns: Namespace, namespaces: NamespaceInfo) -> Namespace { - let out = KI - .run_command( - "ip", - &["netns", "exec", &ns.get_name(), "wg", "show", "wg_exit"], - ) - .unwrap(); + let out = run_command( + "ip", + &["netns", "exec", &ns.get_name(), "wg", "show", "wg_exit"], + ) + .unwrap(); let out = from_utf8(&out.stdout).unwrap(); let out = out.split('\n').collect::>(); diff --git a/integration_tests/src/setup_utils/babel.rs b/integration_tests/src/setup_utils/babel.rs index 22f41fa66..31b6afb4a 100644 --- a/integration_tests/src/setup_utils/babel.rs +++ b/integration_tests/src/setup_utils/babel.rs @@ -1,4 +1,4 @@ -use althea_kernel_interface::KI; +use althea_kernel_interface::run_command; use log::info; use std::{fs::remove_file, path::Path, thread, time::Duration}; @@ -15,7 +15,7 @@ pub fn spawn_babel(ns: String, babelconf_path: String, babeld_path: String) { let babeld_pid = pid_path; let babeld_log = format!("/var/log/babeld-{ns}.log"); // 1 here is for log - let res = KI.run_command( + let res = run_command( "ip", &[ "netns", diff --git a/integration_tests/src/setup_utils/database.rs b/integration_tests/src/setup_utils/database.rs index 4c1efee25..57375dad1 100644 --- a/integration_tests/src/setup_utils/database.rs +++ b/integration_tests/src/setup_utils/database.rs @@ -1,4 +1,4 @@ -use althea_kernel_interface::KI; +use althea_kernel_interface::run_command; use diesel::{Connection, PgConnection}; use log::warn; use std::io::Write; @@ -51,18 +51,17 @@ pub fn start_postgres() { // only init and launch if postgres has not already been started if !Path::new(&postgres_pid_path).exists() { // initialize the db datadir - let res = KI - .run_command( - "sudo", - &[ - "-u", - POSTGRES_USER, - initdb_bin, - "-D", - POSTGRES_DATABASE_LOCATION, - ], - ) - .unwrap(); + let res = run_command( + "sudo", + &[ + "-u", + POSTGRES_USER, + initdb_bin, + "-D", + POSTGRES_DATABASE_LOCATION, + ], + ) + .unwrap(); if !res.status.success() { panic!("Failed to init postgres {:?}", res); } @@ -84,18 +83,17 @@ pub fn start_postgres() { // start postgres in it's own thread, we kill it every time we startup // so it's spawned in this context thread::spawn(move || { - let res = KI - .run_command( - "sudo", - &[ - "-u", - POSTGRES_USER, - postgres_bin, - "-D", - POSTGRES_DATABASE_LOCATION, - ], - ) - .unwrap(); + let res = run_command( + "sudo", + &[ + "-u", + POSTGRES_USER, + postgres_bin, + "-D", + POSTGRES_DATABASE_LOCATION, + ], + ) + .unwrap(); panic!("Postgres has crashed {:?}", res); }); @@ -110,9 +108,8 @@ pub fn start_postgres() { } // reset database contents for every run, this is in the loop becuase it too must wait until the db has started - KI.run_command("psql", &["-c", "drop database test;", "-U", POSTGRES_USER]) - .unwrap(); - KI.run_command( + run_command("psql", &["-c", "drop database test;", "-U", POSTGRES_USER]).unwrap(); + run_command( "psql", &["-c", "create database test;", "-U", POSTGRES_USER], ) diff --git a/integration_tests/src/setup_utils/namespaces.rs b/integration_tests/src/setup_utils/namespaces.rs index 018b9f142..39c4106cf 100644 --- a/integration_tests/src/setup_utils/namespaces.rs +++ b/integration_tests/src/setup_utils/namespaces.rs @@ -1,4 +1,4 @@ -use althea_kernel_interface::{KernelInterfaceError, KI}; +use althea_kernel_interface::{run_command, KernelInterfaceError}; use nix::{ fcntl::{open, OFlag}, sys::stat::Mode, @@ -119,12 +119,12 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { // arbitrary number for the IP assignment let mut counter = 6; // clear namespaces - KI.run_command("ip", &["-all", "netns", "delete", "||", "true"])?; + run_command("ip", &["-all", "netns", "delete", "||", "true"])?; // add namespaces for ns in spaces.names.iter() { - KI.run_command("ip", &["netns", "add", &ns.get_name()])?; + run_command("ip", &["netns", "add", &ns.get_name()])?; // ip netns exec nB ip link set dev lo up - KI.run_command( + run_command( "ip", &[ "netns", @@ -139,7 +139,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { ], )?; // nft create table inet fw4 - KI.run_command( + run_command( "ip", &[ "netns", @@ -155,7 +155,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { // nft add chain inet fw4 input { type filter hook input priority filter; policy accept; } // nft add chain inet fw4 output { type filter hook output priority filter; policy accept; } // nft add chain inet fw4 forward { type filter hook forward priority filter; policy accept; } - KI.run_command( + run_command( "ip", &[ "netns", @@ -179,7 +179,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { "}", ], )?; - KI.run_command( + run_command( "ip", &[ "netns", @@ -203,7 +203,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { "}", ], )?; - KI.run_command( + run_command( "ip", &[ "netns", @@ -253,10 +253,10 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { // delete the old native namespace veth for repeated runs // this veth should go away on it's own when the veths are cleared but that // can take long enough to cause a race condition - KI.run_command("ip", &["link", "del", &veth_native_to_exit])?; + run_command("ip", &["link", "del", &veth_native_to_exit])?; // create link between exit and native namespace - KI.run_command( + run_command( "ip", &[ "link", @@ -270,7 +270,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { ], )?; // set the exit side of the link to the correct namespace the other side is native - KI.run_command( + run_command( "ip", &[ "link", @@ -280,8 +280,8 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { &name.get_name(), ], )?; - KI.run_command("ip", &["link", "set", "up", &veth_native_to_exit])?; - KI.run_command( + run_command("ip", &["link", "set", "up", &veth_native_to_exit])?; + run_command( "ip", &[ "netns", @@ -295,7 +295,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { ], )?; // add ip address for the exit - KI.run_command( + run_command( "ip", &[ "netns", @@ -310,7 +310,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { ], )?; // set default route - KI.run_command( + run_command( "ip", &[ "netns", @@ -331,14 +331,14 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { // now we need to loop the interfaces we created and build a single bridge interface // this will make our job of forwarding their traffic to the internet much easier - KI.run_command("ip", &["link", "add", BRIDGE_NAME, "type", "bridge"])?; + run_command("ip", &["link", "add", BRIDGE_NAME, "type", "bridge"])?; for iface in links_to_native_namespace { - KI.run_command("ip", &["link", "set", &iface, "master", BRIDGE_NAME])?; + run_command("ip", &["link", "set", &iface, "master", BRIDGE_NAME])?; } - KI.run_command("ip", &["link", "set", "up", BRIDGE_NAME])?; - KI.run_command("ip", &["addr", "add", BRIDGE_IP_PREFIX, "dev", BRIDGE_NAME])?; + run_command("ip", &["link", "set", "up", BRIDGE_NAME])?; + run_command("ip", &["addr", "add", BRIDGE_IP_PREFIX, "dev", BRIDGE_NAME])?; // finally we setup a nat between this bridge and the native namespace, allowing traffic to exit - KI.run_command( + run_command( "iptables", &[ "-A", @@ -355,7 +355,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { "ACCEPT", ], )?; - KI.run_command( + run_command( "iptables", &[ "-A", @@ -368,7 +368,7 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { "ACCEPT", ], )?; - KI.run_command( + run_command( "iptables", &[ "-t", @@ -395,24 +395,24 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { counter += 1; // create veth to link them - KI.run_command( + run_command( "ip", &[ "link", "add", &veth_ab, "type", "veth", "peer", "name", &veth_ba, ], )?; // assign each side of the veth to one of the nodes namespaces - KI.run_command("ip", &["link", "set", &veth_ab, "netns", &a_name])?; - KI.run_command("ip", &["link", "set", &veth_ba, "netns", &b_name])?; + run_command("ip", &["link", "set", &veth_ab, "netns", &a_name])?; + run_command("ip", &["link", "set", &veth_ba, "netns", &b_name])?; // add ip addresses on each side - KI.run_command( + run_command( "ip", &[ "netns", "exec", &a_name, "ip", "addr", "add", &ip_ab, "dev", &veth_ab, ], )?; - KI.run_command( + run_command( "ip", &[ "netns", "exec", &b_name, "ip", "addr", "add", &ip_ba, "dev", &veth_ba, @@ -420,14 +420,14 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { )?; // bring the interfaces up - KI.run_command( + run_command( "ip", &[ "netns", "exec", &a_name, "ip", "link", "set", "dev", &veth_ab, "up", ], )?; - KI.run_command( + run_command( "ip", &[ "netns", "exec", &b_name, "ip", "link", "set", "dev", &veth_ba, "up", @@ -436,13 +436,13 @@ pub fn setup_ns(spaces: NamespaceInfo) -> Result<(), KernelInterfaceError> { // ip netns exec nC ip route add 192.168.0.0/24 dev veth-nC-nA // add routes to each other's subnets - KI.run_command( + run_command( "ip", &[ "netns", "exec", &a_name, "ip", "route", "add", &subnet_b, "dev", &veth_ab, ], )?; - KI.run_command( + run_command( "ip", &[ "netns", "exec", &b_name, "ip", "route", "add", &subnet_a, "dev", &veth_ba, diff --git a/integration_tests/src/utils.rs b/integration_tests/src/utils.rs index 9f6310053..95c30959c 100644 --- a/integration_tests/src/utils.rs +++ b/integration_tests/src/utils.rs @@ -8,7 +8,7 @@ use crate::{ }; use actix_rt::time::sleep; use actix_rt::System; -use althea_kernel_interface::KI; +use althea_kernel_interface::run_command; use althea_proto::cosmos_sdk_proto::cosmos::gov::v1beta1::VoteOption; use althea_proto::{ canto::erc20::v1::RegisterCoinProposal, @@ -239,12 +239,11 @@ fn test_reach(from: Namespace, to: Namespace) -> bool { from.get_name(), to.get_name() ); - let output = KI - .run_command( - "ip", - &["netns", "exec", &from.get_name(), "ping6", &ip, "-c", "1"], - ) - .expect(&errormsg); + let output = run_command( + "ip", + &["netns", "exec", &from.get_name(), "ping6", &ip, "-c", "1"], + ) + .expect(&errormsg); let output = from_utf8(&output.stdout).expect("could not get output for ping6!"); trace!("ping output: {output:?} end"); output.contains("1 packets transmitted, 1 received, 0% packet loss") @@ -315,20 +314,19 @@ pub fn test_all_internet_connectivity(namespaces: NamespaceInfo) { for ns in namespaces.names { let start = Instant::now(); loop { - let out = KI - .run_command( - "ip", - &[ - "netns", - "exec", - &ns.get_name(), - "ping", - &NODE_IP.to_string(), - "-c", - "1", - ], - ) - .unwrap(); + let out = run_command( + "ip", + &[ + "netns", + "exec", + &ns.get_name(), + "ping", + &NODE_IP.to_string(), + "-c", + "1", + ], + ) + .unwrap(); if String::from_utf8(out.stdout) .unwrap() .contains("1 received") @@ -554,14 +552,13 @@ pub fn generate_traffic(from: Namespace, to: Option, data: String) { thread::spawn(move || { info!("In new thread about to start iperf server"); let output = if let Some(ns) = to { - KI.run_command( + run_command( "ip", &["netns", "exec", &ns.get_name(), "iperf3", "-s", "-1"], ) .expect("Could not setup iperf server") } else { - KI.run_command("iperf3", &["-s", "-1"]) - .expect("Could not setup iperf server") + run_command("iperf3", &["-s", "-1"]).expect("Could not setup iperf server") }; let stderr = from_utf8(&output.stderr).expect("Why is this failing"); @@ -580,21 +577,20 @@ pub fn generate_traffic(from: Namespace, to: Option, data: String) { info!("Going to setup client"); let ticker = Instant::now(); loop { - let output = KI - .run_command( - "ip", - &[ - "netns", - "exec", - &from.get_name(), - "iperf3", - "-c", - &ip, - "-n", - &data, - ], - ) - .expect("Could not setup iperf client"); + let output = run_command( + "ip", + &[ + "netns", + "exec", + &from.get_name(), + "iperf3", + "-c", + &ip, + "-n", + &data, + ], + ) + .expect("Could not setup iperf client"); let stderr = from_utf8(&output.stderr).expect("Why is this failing"); let std_output = from_utf8(&output.stdout).expect("could not get output for client setup!"); diff --git a/rita_bin/Cargo.toml b/rita_bin/Cargo.toml index 9895030cb..fbf8f5e17 100644 --- a/rita_bin/Cargo.toml +++ b/rita_bin/Cargo.toml @@ -40,7 +40,6 @@ arrayvec = { version = "0.7", features = ["serde"] } hex-literal = "0.4" openssl-probe = "0.1" env_logger = "0.11" -lazy_static = "1.5" phonenumber = "0.3.6" r2d2 = "0.8" ctrlc = { version = "3.4.5", features = ["termination"] } diff --git a/rita_bin/src/client.rs b/rita_bin/src/client.rs index 6069e152f..7a44f820e 100644 --- a/rita_bin/src/client.rs +++ b/rita_bin/src/client.rs @@ -11,19 +11,18 @@ #![allow(clippy::pedantic)] #![forbid(unsafe_code)] +use althea_kernel_interface::babel::restart_babel; +use althea_kernel_interface::check_cron::check_cron; +use althea_kernel_interface::is_openwrt::is_openwrt; #[cfg(feature = "jemalloc")] use jemallocator::Jemalloc; #[cfg(feature = "jemalloc")] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -#[macro_use] -extern crate lazy_static; #[macro_use] extern crate log; -use althea_kernel_interface::KernelInterface; -use althea_kernel_interface::LinuxCommandRunner; use docopt::Docopt; use rita_client::dashboard::start_client_dashboard; use rita_client::get_client_usage; @@ -45,10 +44,6 @@ use settings::client::RitaClientSettings; use settings::save_settings_on_shutdown; use settings::FileWrite; -lazy_static! { - pub static ref KI: Box = Box::new(LinuxCommandRunner {}); -} - fn main() { //Setup a SIGTERM hadler ctrlc::set_handler(move || { @@ -130,7 +125,7 @@ fn main() { // the old tunnels is now in an incorrect state. We must either restart babel or empty it's interfaces list so that the newly // created wireguard tunnels can be re-added by this instance of Rita. Due to errors in babel (see git history there) // restarting is the way to go as removing dead interfaces often does not work - KI.restart_babel(); + restart_babel(); apply_babeld_settings_defaults( settings.network.babel_port, settings.network.babeld_settings, @@ -167,7 +162,7 @@ fn main() { } // If we are an an OpenWRT device try and rescue it from update issues - if KI.is_openwrt() && KI.check_cron().is_err() { + if is_openwrt() && check_cron().is_err() { error!("Failed to setup cron!"); } diff --git a/rita_client/src/dashboard/bandwidth_limit.rs b/rita_client/src/dashboard/bandwidth_limit.rs index a48639bd6..a5c7a1017 100644 --- a/rita_client/src/dashboard/bandwidth_limit.rs +++ b/rita_client/src/dashboard/bandwidth_limit.rs @@ -4,7 +4,8 @@ use actix_web_async::http::StatusCode; use actix_web_async::HttpResponse; use actix_web_async::{web::Path, HttpRequest}; -use rita_common::{RitaCommonError, KI}; +use althea_kernel_interface::traffic_control::set_codel_shaping; +use rita_common::RitaCommonError; pub async fn get_bandwidth_limit(_req: HttpRequest) -> HttpResponse { let val = settings::get_rita_client().network.user_bandwidth_limit; @@ -23,7 +24,7 @@ pub async fn set_bandwidth_limit(path: Path) -> HttpResponse { } else { return HttpResponse::BadRequest().finish(); } - let _res = KI.set_codel_shaping("br-lan", network.user_bandwidth_limit); + let _res = set_codel_shaping("br-lan", network.user_bandwidth_limit); rita_client.network = network; settings::set_rita_client(rita_client); diff --git a/rita_client/src/dashboard/devices_on_lan.rs b/rita_client/src/dashboard/devices_on_lan.rs index 61a54bbc9..673802c34 100644 --- a/rita_client/src/dashboard/devices_on_lan.rs +++ b/rita_client/src/dashboard/devices_on_lan.rs @@ -1,9 +1,13 @@ use crate::dashboard::extend_hardware_info; use actix_web_async::{HttpRequest, HttpResponse}; -use althea_kernel_interface::hardware_info::get_hardware_info; +use althea_kernel_interface::{ + hardware_info::get_hardware_info, + interface_tools::{get_ip_from_iface, get_ipv6_from_iface}, + ip_neigh::grab_ip_neigh, + run_command, +}; use althea_types::HardwareInfo; use mac_address::MacAddress; -use rita_common::KI; use serde::Serializer; use std::{ collections::{HashMap, HashSet}, @@ -27,8 +31,8 @@ fn consolidate_wlan_arp_table( /// Used to prune all devices not part of the lan by using the netmask to delete invalid ips fn prune_non_lan_entries(mut arp_table: Vec<(IpAddr, MacAddress)>) -> Vec<(IpAddr, MacAddress)> { - let ip4 = KI.get_ip_from_iface("br-lan"); - let ip6 = KI.get_ipv6_from_iface("br-lan"); + let ip4 = get_ip_from_iface("br-lan"); + let ip6 = get_ipv6_from_iface("br-lan"); // The long set of calls essentially grabs each ipaddress out of the arp table and checks if there is a singular // address match within each of the interfaces for ipv4 or ipv6. Otherwise, it prunes the ipaddress out of the arp @@ -163,7 +167,7 @@ pub fn generate_lan_device( /// if it isn't it simply returns its mac addresses to use fn resolve_name(ip_addresses: HashSet, mac_addr: MacAddress) -> String { info!("Sending cat command to kernel"); - let res = KI.run_command("cat", &["/tmp/dhcp.leases"]); + let res = run_command("cat", &["/tmp/dhcp.leases"]); match res { Ok(output) => { if !output.stdout.is_empty() { @@ -206,7 +210,7 @@ fn resolve_name(ip_addresses: HashSet, mac_addr: MacAddress) -> String { /// This is an endpoint to grab all the lan devices mapping to ip address and /// returns the json request populated with the related hardware information pub async fn get_devices_lan_endpoint(_req: HttpRequest) -> HttpResponse { - let command_response = KI.grab_ip_neigh(); + let command_response = grab_ip_neigh(); match command_response { Ok(output) => { let arp_table = output; diff --git a/rita_client/src/dashboard/exits.rs b/rita_client/src/dashboard/exits.rs index 2a720a5e7..563b28a3c 100644 --- a/rita_client/src/dashboard/exits.rs +++ b/rita_client/src/dashboard/exits.rs @@ -5,11 +5,11 @@ use crate::exit_manager::requests::exit_setup_request; use crate::RitaClientError; use actix_web_async::http::StatusCode; use actix_web_async::{web::Path, HttpRequest, HttpResponse}; +use althea_kernel_interface::ping_check::ping_check; use althea_types::{ExitIdentity, ExitState}; use babel_monitor::open_babel_stream; use babel_monitor::parse_routes; use babel_monitor::parsing::do_we_have_route; -use rita_common::KI; use std::collections::HashMap; use std::time::Duration; @@ -44,13 +44,12 @@ fn is_tunnel_working( ) -> bool { match (current_exit.clone(), is_selected(exit, current_exit)) { (Some(_exit), true) => match exit_status.general_details() { - Some(details) => KI - .ping_check( - &details.server_internal_ip, - EXIT_PING_TIMEOUT, - Some("wg_exit"), - ) - .unwrap_or(false), + Some(details) => ping_check( + &details.server_internal_ip, + EXIT_PING_TIMEOUT, + Some("wg_exit"), + ) + .unwrap_or(false), None => false, }, (_, _) => false, @@ -79,7 +78,7 @@ pub fn dashboard_get_exit_info() -> Result, RitaClientError> { // failed pings block for one second, so we should be sure it's at least reasonable // to expect the pings to work before issuing them. let reachable = if have_route { - KI.ping_check(&route_ip, EXIT_PING_TIMEOUT, None)? + ping_check(&route_ip, EXIT_PING_TIMEOUT, None)? } else { false }; diff --git a/rita_client/src/dashboard/router.rs b/rita_client/src/dashboard/router.rs index c26672bb4..d9c1c544f 100644 --- a/rita_client/src/dashboard/router.rs +++ b/rita_client/src/dashboard/router.rs @@ -1,7 +1,7 @@ use crate::operator_update::updater::update_system; use actix_web_async::{http::StatusCode, HttpRequest, HttpResponse}; +use althea_kernel_interface::{is_openwrt::is_openwrt, run_command}; use althea_types::UpdateType; -use rita_common::KI; use std::sync::{Arc, RwLock}; lazy_static! { @@ -10,8 +10,8 @@ lazy_static! { } pub async fn reboot_router(_req: HttpRequest) -> HttpResponse { - if KI.is_openwrt() { - if let Err(e) = KI.run_command("reboot", &[]) { + if is_openwrt() { + if let Err(e) = run_command("reboot", &[]) { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR) .json(format!("Cannot run reboot: {e}")); } @@ -25,7 +25,7 @@ pub async fn reboot_router(_req: HttpRequest) -> HttpResponse { /// the lazy static variable and use this to perform a sysupgrade. If device is not openwrt or no image /// link is available, do nothing pub async fn update_router(_req: HttpRequest) -> HttpResponse { - if KI.is_openwrt() { + if is_openwrt() { let reader = &*UPDATE_INSTRUCTION.read().unwrap(); if reader.is_none() { return HttpResponse::Ok().json("No update instructions set, doing nothing"); diff --git a/rita_client/src/exit_manager/exit_loop.rs b/rita_client/src/exit_manager/exit_loop.rs index da60bcd2c..7eb8cc0cf 100644 --- a/rita_client/src/exit_manager/exit_loop.rs +++ b/rita_client/src/exit_manager/exit_loop.rs @@ -12,10 +12,12 @@ use crate::exit_manager::utils::{ use crate::heartbeat::get_exit_registration_state; use crate::traffic_watcher::{query_exit_debts, QueryExitDebts}; use actix_async::System as AsyncSystem; +use althea_kernel_interface::ip_addr::setup_ipv6_slaac as setup_ipv6_slaac_ki; +use althea_kernel_interface::ip_route::get_default_route; +use althea_kernel_interface::run_command; use althea_types::{ExitDetails, ExitListV2}; use althea_types::{ExitIdentity, ExitState}; use rita_common::blockchain_oracle::low_balance; -use rita_common::KI; use std::net::IpAddr; use std::thread; use std::time::{Duration, Instant}; @@ -67,7 +69,7 @@ pub fn start_exit_manager_loop() { ); if Instant::now() - last_restart < Duration::from_secs(60) { error!("Restarting too quickly, rebooting instead!"); - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } last_restart = Instant::now(); } @@ -171,7 +173,7 @@ fn setup_exit_tunnel( ); let signed_up_for_exit = registration_state.our_details(); - let default_route = match KI.get_default_route() { + let default_route = match get_default_route() { Ok(a) => a, Err(e) => { error!("Failed to get default route: {:?}", e); @@ -322,6 +324,6 @@ fn setup_ipv6_slaac() { trace!("Setting up ipv6 for slaac"); if let Some(ipv6_sub) = get_client_pub_ipv6() { trace!("setting up slaac with {:?}", ipv6_sub); - KI.setup_ipv6_slaac(ipv6_sub) + setup_ipv6_slaac_ki(ipv6_sub) } } diff --git a/rita_client/src/exit_manager/time_sync.rs b/rita_client/src/exit_manager/time_sync.rs index 7d7f3e679..c932fb306 100644 --- a/rita_client/src/exit_manager/time_sync.rs +++ b/rita_client/src/exit_manager/time_sync.rs @@ -1,4 +1,4 @@ -use althea_kernel_interface::KI; +use althea_kernel_interface::{setup_wg_if::get_last_handshake_time, time::set_local_time}; use althea_types::{ExitIdentity, ExitSystemTime}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -42,7 +42,7 @@ pub async fn get_exit_time(exit: ExitIdentity) -> Option { // try to get the latest handshake for the wg_exit tunnel pub fn get_latest_exit_handshake() -> Option { - match KI.get_last_handshake_time("wg_exit") { + match get_last_handshake_time("wg_exit") { Ok(results) => results.first().map(|(_, time)| { info!("last exit handshake {:?}", time); *time @@ -90,7 +90,7 @@ pub async fn maybe_set_local_to_exit_time(exit: ExitIdentity) { if let Ok(diff) = exit_time.duration_since(now) { if diff > MAX_DIFF_LOCAL_EXIT_TIME { // if we're here, then it's time to set our time - if KI.set_local_time(exit_time).is_ok() { + if set_local_time(exit_time).is_ok() { info!("Local time was reset to the exit's time: {:?}", exit_time); } } diff --git a/rita_client/src/exit_manager/utils.rs b/rita_client/src/exit_manager/utils.rs index e32322ee8..3f27535bb 100644 --- a/rita_client/src/exit_manager/utils.rs +++ b/rita_client/src/exit_manager/utils.rs @@ -4,6 +4,12 @@ use crate::heartbeat::get_exit_registration_state; use crate::rita_loop::CLIENT_LOOP_TIMEOUT; use crate::RitaClientError; use actix_web_async::Result; +use althea_kernel_interface::exit_client_tunnel::{ + block_client_nat, create_client_nat_rules, restore_client_nat, set_client_exit_tunnel_config, + set_ipv6_route_to_tunnel, set_route_to_tunnel, +}; +use althea_kernel_interface::ip_route::update_settings_route; +use althea_kernel_interface::setup_wg_if::create_blank_wg_interface; use althea_kernel_interface::{ exit_client_tunnel::ClientExitTunnelConfig, DefaultRoute, KernelInterfaceError, }; @@ -16,7 +22,6 @@ use babel_monitor::open_babel_stream; use babel_monitor::parse_routes; use babel_monitor::structs::Route; use ipnetwork::IpNetwork; -use rita_common::KI; use settings::set_rita_client; use std::net::SocketAddr; @@ -30,10 +35,10 @@ pub fn linux_setup_exit_tunnel( let local_mesh_ip = network.mesh_ip; // TODO this should be refactored to return a value - KI.update_settings_route(&mut network.last_default_route)?; + update_settings_route(&mut network.last_default_route)?; info!("Updated settings route"); - if let Err(KernelInterfaceError::RuntimeError(v)) = KI.create_blank_wg_interface("wg_exit") { + if let Err(KernelInterfaceError::RuntimeError(v)) = create_blank_wg_interface("wg_exit") { return Err(RitaClientError::MiscStringError(v)); } @@ -53,23 +58,23 @@ pub fn linux_setup_exit_tunnel( rita_client.network = network; settings::set_rita_client(rita_client); - KI.set_client_exit_tunnel_config(args, local_mesh_ip)?; - KI.set_route_to_tunnel(&general_details.server_internal_ip)?; - KI.set_ipv6_route_to_tunnel()?; + set_client_exit_tunnel_config(args, local_mesh_ip)?; + set_route_to_tunnel(&general_details.server_internal_ip)?; + set_ipv6_route_to_tunnel()?; - KI.create_client_nat_rules()?; + create_client_nat_rules()?; Ok(()) } pub fn restore_nat() { - if let Err(e) = KI.restore_client_nat() { + if let Err(e) = restore_client_nat() { error!("Failed to restore client nat! {:?}", e); } } pub fn remove_nat() { - if let Err(e) = KI.block_client_nat() { + if let Err(e) = block_client_nat() { error!("Failed to block client nat! {:?}", e); } } diff --git a/rita_client/src/heartbeat/mod.rs b/rita_client/src/heartbeat/mod.rs index 4abac405d..1d58b6ced 100644 --- a/rita_client/src/heartbeat/mod.rs +++ b/rita_client/src/heartbeat/mod.rs @@ -13,7 +13,7 @@ //! This packet is encrypted using the usual LibSodium box construction and sent to the heartbeat server in the following format //! WgKey, Nonce, Ciphertext for the HeartBeatMessage. This consumes 32 bytes, 24 bytes, and to the end of the message -use althea_kernel_interface::KI; +use althea_kernel_interface::run_command; use althea_types::ExitDetails; use althea_types::ExitState; use althea_types::HeartbeatMessage; @@ -110,7 +110,7 @@ pub fn send_heartbeat_loop() { error!("Heartbeat loop thread panicked! Respawning {:?}", e); if Instant::now() - last_restart < Duration::from_secs(60) { error!("Restarting too quickly, rebooting instead!"); - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } last_restart = Instant::now(); } diff --git a/rita_client/src/operator_update/mod.rs b/rita_client/src/operator_update/mod.rs index fcfaeb99d..d57e5657b 100644 --- a/rita_client/src/operator_update/mod.rs +++ b/rita_client/src/operator_update/mod.rs @@ -10,6 +10,7 @@ use crate::heartbeat::HEARTBEAT_SERVER_KEY; use crate::rita_loop::is_gateway_client; use crate::RitaClientError; use althea_kernel_interface::hardware_info::get_hardware_info; +use althea_kernel_interface::run_command; use althea_types::websockets::{ OperatorAction, OperatorWebsocketMessage, OperatorWebsocketResponse, PaymentAndNetworkSettings, }; @@ -29,7 +30,6 @@ use rita_common::tunnel_manager::shaping::flag_reset_shaper; use rita_common::usage_tracker::structs::UsageType::{Client, Relay}; use rita_common::usage_tracker::{get_current_hour, get_current_throughput, get_usage_data_map}; use rita_common::DROPBEAR_AUTHORIZED_KEYS; -use rita_common::KI; use serde_json::Map; use serde_json::Value; use settings::client::RitaClientSettings; @@ -268,11 +268,11 @@ fn perform_operator_action(action: OperatorAction) { match action { OperatorAction::ResetShaper => flag_reset_shaper(), OperatorAction::Reboot => { - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } OperatorAction::SoftReboot => { let args = vec!["restart"]; - if let Err(e) = KI.run_command("/etc/init.d/rita", &args) { + if let Err(e) = run_command("/etc/init.d/rita", &args) { error!("Unable to restart rita after opkg update: {}", e) } } @@ -365,7 +365,7 @@ fn update_authorized_keys( let mut existing = HashSet::new(); let auth_keys_file = File::open(keys_file); let mut write_data: Vec = vec![]; - let temp_key_file = String::from("temp_authorized_keys"); + let temp_key_file = format!("temp_authorized_keys_{}", keys_file); info!( "Authorized keys updates add {} remove {} pubkeys", diff --git a/rita_client/src/operator_update/tests.rs b/rita_client/src/operator_update/tests.rs index 617f19d35..97e6e87dd 100644 --- a/rita_client/src/operator_update/tests.rs +++ b/rita_client/src/operator_update/tests.rs @@ -59,7 +59,7 @@ mod test { let key_file: &str = "authorized_keys"; let operator_key = touch_temp_file(key_file); - let _update = update_authorized_keys(added_keys.clone(), removed_keys, key_file); + update_authorized_keys(added_keys.clone(), removed_keys, key_file).unwrap(); let result = parse_keys(key_file); assert_eq!(result.len(), 2); assert!(result.contains(&added_keys[0])); @@ -77,7 +77,7 @@ mod test { let operator_key = touch_temp_file(key_file); - let _update = update_authorized_keys(added_keys.clone(), removed_keys, key_file); + update_authorized_keys(added_keys.clone(), removed_keys, key_file).unwrap(); let result = parse_keys(key_file); assert!(result.contains(&added_keys[0])); assert!(result.contains(&added_keys[1])); @@ -97,7 +97,7 @@ mod test { let operator_key = touch_temp_file(key_file); - let _update = update_authorized_keys(added_keys, removed_keys, key_file); + update_authorized_keys(added_keys, removed_keys, key_file).unwrap(); let result = parse_keys(key_file); assert!(result.contains(&operator_key.to_string())); @@ -112,7 +112,7 @@ mod test { let operator_key = touch_temp_file(key_file); let removed_keys = vec![String::from(operator_key)]; - let _update = update_authorized_keys(added_keys, removed_keys.clone(), key_file); + update_authorized_keys(added_keys, removed_keys.clone(), key_file).unwrap(); let result = parse_keys(key_file); for item in result { @@ -128,7 +128,7 @@ mod test { ]; let removed_keys: Vec = vec![]; let key_file: &str = "create_keys_file"; - let _update = update_authorized_keys(added_keys, removed_keys, key_file); + update_authorized_keys(added_keys, removed_keys, key_file).unwrap(); assert!(Path::new(key_file).exists()); } #[test] diff --git a/rita_client/src/operator_update/updater.rs b/rita_client/src/operator_update/updater.rs index 536381fe9..ca84f29fb 100644 --- a/rita_client/src/operator_update/updater.rs +++ b/rita_client/src/operator_update/updater.rs @@ -2,21 +2,25 @@ //! versus updating operator tools on the status of this router which is the context of 'update' in the rest //! of this module -use althea_kernel_interface::KernelInterfaceError; +use althea_kernel_interface::{ + is_openwrt::is_openwrt, + run_command, + upgrade::{perform_opkg, perform_sysupgrade}, + KernelInterfaceError, +}; use althea_types::UpdateType; -use rita_common::KI; /// Updates the system, including Rita and other packages by performing either a sysupgrade or opkg install pub fn update_system(instruction: UpdateType) -> Result<(), KernelInterfaceError> { - if KI.is_openwrt() { + if is_openwrt() { match instruction { - UpdateType::Sysupgrade(command) => match KI.perform_sysupgrade(command) { + UpdateType::Sysupgrade(command) => match perform_sysupgrade(command) { Ok(_) => Ok(()), Err(e) => Err(e), }, UpdateType::Opkg(commands) => { for cmd in commands { - let res = KI.perform_opkg(cmd); + let res = perform_opkg(cmd); match res { Ok(o) => match o.status.code() { Some(0) => info!("opkg completed successfully! {:?}", o), @@ -35,10 +39,10 @@ pub fn update_system(instruction: UpdateType) -> Result<(), KernelInterfaceError // Restart rita after opkg let args = vec!["restart"]; - if let Err(e) = KI.run_command("/etc/init.d/rita", &args) { + if let Err(e) = run_command("/etc/init.d/rita", &args) { error!("Unable to restart rita after opkg update: {}", e) } - if let Err(e) = KI.run_command("/etc/init.d/rita_tower", &args) { + if let Err(e) = run_command("/etc/init.d/rita_tower", &args) { error!("Unable to restart rita tower after opkg update: {}", e) } diff --git a/rita_client/src/rita_loop/mod.rs b/rita_client/src/rita_loop/mod.rs index 40eb1d0e7..b9023689e 100644 --- a/rita_client/src/rita_loop/mod.rs +++ b/rita_client/src/rita_loop/mod.rs @@ -10,8 +10,18 @@ use crate::heartbeat::send_heartbeat_loop; use crate::heartbeat::HEARTBEAT_SERVER_KEY; use crate::operator_fee_manager::tick_operator_payments; use actix_async::System as AsyncSystem; +use althea_kernel_interface::dns::get_resolv_servers; +use althea_kernel_interface::ip_addr::is_iface_up; +use althea_kernel_interface::ip_route::manual_peers_route; +use althea_kernel_interface::is_openwrt::is_openwrt; +use althea_kernel_interface::manipulate_uci::get_uci_var; +use althea_kernel_interface::manipulate_uci::openwrt_reset_dnsmasq; +use althea_kernel_interface::manipulate_uci::set_uci_list; +use althea_kernel_interface::manipulate_uci::set_uci_var; +use althea_kernel_interface::manipulate_uci::uci_commit; +use althea_kernel_interface::netns::check_integration_test_netns; +use althea_kernel_interface::run_command; use althea_kernel_interface::KernelInterfaceError; -use althea_kernel_interface::KI; use althea_types::ExitState; use antenna_forwarding_client::start_antenna_forwarding_proxy; use rita_common::dashboard::interfaces::get_interfaces; @@ -48,7 +58,7 @@ lazy_static! { } pub fn is_gateway_client() -> bool { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); IS_GATEWAY_CLIENT .read() .unwrap() @@ -59,7 +69,7 @@ pub fn is_gateway_client() -> bool { } pub fn set_gateway_client(input: bool) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); let gw_lock = &mut *IS_GATEWAY_CLIENT.write().unwrap(); // Clippy notation @@ -148,7 +158,7 @@ pub fn start_rita_client_loop() { error!("Rita client loop thread paniced! Respawning {:?}", e); if Instant::now() - last_restart < Duration::from_secs(60) { error!("Restarting too quickly, rebooting instead!"); - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } last_restart = Instant::now(); } @@ -229,7 +239,7 @@ fn manage_gateway() { // the is_up detection is mostly useless because these ports reside on switches which mark // all ports as up all the time. if let Some(external_nic) = settings::get_rita_common().network.external_nic { - if KI.is_iface_up(&external_nic).unwrap_or(false) { + if is_iface_up(&external_nic).unwrap_or(false) { if let Ok(interfaces) = get_interfaces() { info!("We are a Gateway"); // this flag is used to handle billing around the corner case @@ -243,16 +253,13 @@ fn manage_gateway() { if let Some(mode) = interfaces.get(&external_nic) { if matches!(mode, InterfaceMode::Wan | InterfaceMode::StaticWan { .. }) { let mut common = settings::get_rita_common(); - match KI.get_resolv_servers() { + match get_resolv_servers() { Ok(s) => { for ip in s.iter() { trace!("Resolv route {:?}", ip); - KI.manual_peers_route( - ip, - &mut common.network.last_default_route, - ) - .unwrap(); + manual_peers_route(ip, &mut common.network.last_default_route) + .unwrap(); } settings::set_rita_common(common); } @@ -349,7 +356,7 @@ pub fn update_dns_conf() { }; // if we are on openwrt we can automatically configure the dhcp server, otherwise the user is on their own to santiy check their config - if KI.is_openwrt() { + if is_openwrt() { // the goal of this code is to make sure that the router is the only dns server offered during the dhcp negotiation process // not every device will take this dns server and use it, but many do, and some use it exclusively so it has to be correct @@ -360,8 +367,8 @@ pub fn update_dns_conf() { // if it does not start with the exit internal nameserver add it. An empty value is acceptable // since dnsmasq simply uses resolv.conf servers which we update above in that case. match ( - parse_list_to_ip(KI.get_uci_var(DHCP_DNS_LIST_KEY)), - maybe_parse_ip(KI.get_uci_var(LAN_IP_KEY)), + parse_list_to_ip(get_uci_var(DHCP_DNS_LIST_KEY)), + maybe_parse_ip(get_uci_var(LAN_IP_KEY)), ) { (Ok(dns_server_list), Ok(router_internal_ip)) => { // an empty list uses the system resolver, this is acceptable since we just set the system resolver to @@ -389,18 +396,18 @@ fn overwrite_dns_server_and_restart_dhcp(key: &str, ips: Vec) { let slice_of_strs: Vec<&str> = ips.iter().map(|s| s.as_str()).collect(); let reference_to_slice: &[&str] = &slice_of_strs; - let res = KI.set_uci_list(key, reference_to_slice); + let res = set_uci_list(key, reference_to_slice); if let Err(e) = res { error!("Failed to set dhcp server list via uci {:?}", e); return; } - let res = KI.uci_commit(key); + let res = uci_commit(key); if let Err(e) = res { error!("Failed to set dhcp server list via uci {:?}", e); return; } - let res = KI.openwrt_reset_dnsmasq(); + let res = openwrt_reset_dnsmasq(); if let Err(e) = res { error!("Failed to restart dhcp config with {:?}", e); } @@ -411,20 +418,20 @@ fn ensure_dhcp_resolvfile() { const DHCP_RESOLV_FILE_KEY: &str = "dhcp.@dnsmasq[0].resolvfile"; const DHCP_RESOLV_FILE_VALUE: &str = "/etc/resolv.conf"; - match KI.get_uci_var(DHCP_RESOLV_FILE_KEY) { + match get_uci_var(DHCP_RESOLV_FILE_KEY) { Ok(resolv_file) => { if resolv_file != DHCP_RESOLV_FILE_VALUE { - let res = KI.set_uci_var(DHCP_RESOLV_FILE_KEY, DHCP_RESOLV_FILE_VALUE); + let res = set_uci_var(DHCP_RESOLV_FILE_KEY, DHCP_RESOLV_FILE_VALUE); if let Err(e) = res { error!("Failed to set dhcp resolvfile {:?}", e); return; } - let res = KI.uci_commit(DHCP_RESOLV_FILE_KEY); + let res = uci_commit(DHCP_RESOLV_FILE_KEY); if let Err(e) = res { error!("Failed to set dhcp resolvfile {:?}", e); return; } - let res = KI.openwrt_reset_dnsmasq(); + let res = openwrt_reset_dnsmasq(); if let Err(e) = res { error!("Failed to restart dhcp config with {:?}", e); } @@ -456,7 +463,7 @@ pub fn update_system_time() { ); let seconds = last_saved * 3600; let formatted_seconds = format!("@{}", seconds); - match KI.run_command("date", &["-s", &formatted_seconds]) { + match run_command("date", &["-s", &formatted_seconds]) { Ok(_) => info!("System time updated!"), Err(e) => error!("{}", e), } diff --git a/rita_client/src/self_rescue.rs b/rita_client/src/self_rescue.rs index ce7f74624..919ec4a54 100644 --- a/rita_client/src/self_rescue.rs +++ b/rita_client/src/self_rescue.rs @@ -1,6 +1,10 @@ use althea_kernel_interface::hardware_info::get_hardware_info; -use althea_kernel_interface::KI; -use rand::prelude::SliceRandom; +use althea_kernel_interface::ping_check::ping_check; +use althea_kernel_interface::run_command; +use althea_kernel_interface::setup_wg_if::{ + get_last_active_handshake_time, get_list_of_wireguard_interfaces, +}; +use rand::seq::SliceRandom; use rand::Rng; use rita_common::TUNNEL_HANDSHAKE_TIMEOUT; use settings::get_rita_common; @@ -33,7 +37,7 @@ pub fn start_rita_client_rescue_loop() { } else { // If this router has been in a bad state for >10 mins, reboot if (Instant::now() - last_successful_ping) > REBOOT_TIMEOUT { - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } } @@ -45,13 +49,13 @@ pub fn start_rita_client_rescue_loop() { // If you're coming back and looking at expanding/improving this in the future, consider moving this out of a separate thread // and into rita_client or rita_common loops using a shared Instant that exists only in the context of those two threads rather than // a global lazy static Instant. This would keep us from having to make guesses around the system time - let wg_interfaces = KI.get_list_of_wireguard_interfaces(); + let wg_interfaces = get_list_of_wireguard_interfaces(); trace!("interfaces {:?}", wg_interfaces); if let Ok(interfaces) = wg_interfaces { let mut rng = rand::thread_rng(); let sample = interfaces.choose_multiple(&mut rng, 3); for interface in sample { - if let Ok(times) = KI.get_last_active_handshake_time(interface) { + if let Ok(times) = get_last_active_handshake_time(interface) { // we grab only the first timestamps because none of these tunnels should have multiple timestamps if let Some((_, time)) = times.first() { if let Ok(elapsed) = time.elapsed() { @@ -60,7 +64,7 @@ pub fn start_rita_client_rescue_loop() { last_successful_handshake_check.elapsed() > REBOOT_TIMEOUT, ) { (true, true) => { - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } // wait (true, false) => {} @@ -84,7 +88,7 @@ pub fn start_rita_client_rescue_loop() { (Some(mdl), Ok(info)) => { if mdl.contains("mikrotik_hap-ac2") && info.load_avg_fifteen_minute > 4.0 { info!("15 minute load average > 4, rebooting!"); - let _res = KI.run_command("reboot", &[]); + let _res = run_command("reboot", &[]); } } (Some(_), Err(_)) => error!("Could not get hardware info!"), @@ -122,7 +126,7 @@ pub fn run_ping_test() -> bool { let target_ip = PING_TEST_IPS[index]; let timeout = Duration::from_secs(5); - match KI.ping_check(&target_ip.into(), timeout, None) { + match ping_check(&target_ip.into(), timeout, None) { Ok(out) => out, Err(e) => { error!("ipv4 ping error: {:?}", e); diff --git a/rita_client/src/traffic_watcher/mod.rs b/rita_client/src/traffic_watcher/mod.rs index 22b0ca3f5..19f7bb1ae 100644 --- a/rita_client/src/traffic_watcher/mod.rs +++ b/rita_client/src/traffic_watcher/mod.rs @@ -23,6 +23,8 @@ use crate::rita_loop::is_gateway_client; use crate::RitaClientError; +use althea_kernel_interface::netns::check_integration_test_netns; +use althea_kernel_interface::wg_iface_counter::read_wg_counters; use althea_types::Identity; use babel_monitor::parsing::get_installed_route; use babel_monitor::structs::BabelMonitorError; @@ -33,7 +35,6 @@ use rita_common::debt_keeper::{gateway_traffic_update, traffic_replace, traffic_ use rita_common::usage_tracker::structs::UsageType; use rita_common::usage_tracker::update_usage_data; use rita_common::usage_tracker::UpdateUsage; -use rita_common::KI; use std::collections::HashMap; use std::net::IpAddr; use std::sync::Arc; @@ -48,7 +49,7 @@ lazy_static! { /// Gets Traffic watcher copy from the static ref, or default if no value has been set pub fn get_traffic_watcher() -> TrafficWatcher { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); TRAFFIC_WATCHER .read() .unwrap() @@ -64,7 +65,7 @@ pub fn get_traffic_watcher() -> TrafficWatcher { pub fn get_traffic_watcher_write_ref( input: &mut HashMap, ) -> &mut TrafficWatcher { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); input.entry(netns).or_default(); input.get_mut(&netns).unwrap() } @@ -230,7 +231,7 @@ pub fn local_traffic_calculation( let exit_route = find_exit_route_capped(exit.mesh_ip, routes)?; info!("Exit metric: {}", exit_route.metric); - let counter = match KI.read_wg_counters("wg_exit") { + let counter = match read_wg_counters("wg_exit") { Ok(res) => { if res.len() > 1 { warn!("wg_exit client tunnel has multiple peers!"); diff --git a/rita_common/src/dashboard/auth.rs b/rita_common/src/dashboard/auth.rs index 2040f162f..a201d454f 100644 --- a/rita_common/src/dashboard/auth.rs +++ b/rita_common/src/dashboard/auth.rs @@ -1,5 +1,8 @@ -use crate::{RitaCommonError, KI}; +use crate::RitaCommonError; use actix_web_async::{http::StatusCode, web::Json, HttpResponse}; +use althea_kernel_interface::{ + fs_sync::fs_sync, is_openwrt::is_openwrt, set_system_password::set_system_password, +}; use clarity::utils::bytes_to_hex_str; use settings::set_rita_common; use sha3::{Digest, Sha3_512}; @@ -28,13 +31,13 @@ pub async fn set_pass(router_pass: Json) -> HttpResponse { .json(format!("{}", RitaCommonError::SettingsError(e))); } - if KI.is_openwrt() { - if let Err(e) = KI.set_system_password(router_pass.password) { + if is_openwrt() { + if let Err(e) = set_system_password(router_pass.password) { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(format!("{e}")); } // We edited disk contents, force global sync - if let Err(e) = KI.fs_sync() { + if let Err(e) = fs_sync() { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(format!("{e}")); } } diff --git a/rita_common/src/dashboard/interfaces.rs b/rita_common/src/dashboard/interfaces.rs index 6c39bd38d..2d2ecb6b9 100644 --- a/rita_common/src/dashboard/interfaces.rs +++ b/rita_common/src/dashboard/interfaces.rs @@ -1,8 +1,14 @@ //! A generalized interface for modifying networking interface assignments using UCI -use crate::{RitaCommonError, KI}; +use crate::RitaCommonError; use actix_web_async::http::StatusCode; use actix_web_async::web::Path; use actix_web_async::{web::Json, HttpRequest, HttpResponse}; +use althea_kernel_interface::fs_sync::fs_sync; +use althea_kernel_interface::manipulate_uci::{ + del_uci_var, get_uci_var, openwrt_reset_network, openwrt_reset_wireless, set_uci_var, + uci_commit, uci_revert, uci_show, +}; +use althea_kernel_interface::{is_openwrt::is_openwrt, run_command}; use std::collections::HashMap; use std::fmt::Display; use std::net::Ipv4Addr; @@ -63,7 +69,7 @@ pub fn get_interfaces() -> Result, RitaCommonErro let mut retval = HashMap::new(); // Wired - for (setting_name, value) in KI.uci_show(Some("network"))? { + for (setting_name, value) in uci_show(Some("network"))? { // Only non-loopback non-bridge interface names should get past if setting_name.contains("ifname") && !value.contains("backhaul") && value != "lo" { // it's a list and we need to handle that @@ -108,7 +114,7 @@ pub fn ethernet2mode(ifname: &str, setting_name: &str) -> Result InterfaceMode::LTE, s if s.contains("backhaul") => { let prefix = "network.backhaul"; - let backhaul = KI.uci_show(Some(prefix))?; + let backhaul = uci_show(Some(prefix))?; trace!("{:?}", backhaul); let proto = if let Some(val) = backhaul.get(&format!("{prefix}.proto")) { val @@ -221,7 +227,7 @@ fn multiset_interfaces( trace!("Successfully transformed ethernet mode, rebooting"); // reboot has been moved here to avoid doing it after every interface, in theory we could do this without rebooting // and some attention has been paid to maintaining that possibility - KI.run_command("reboot", &[])?; + run_command("reboot", &[])?; Ok(()) } @@ -259,23 +265,23 @@ pub fn ethernet_transform_mode( InterfaceMode::Wan | InterfaceMode::StaticWan { .. } => { network.external_nic = None; - let ret = KI.del_uci_var("network.backhaul"); + let ret = del_uci_var("network.backhaul"); return_codes.push(ret); } // LTE is the same InterfaceMode::LTE => { network.external_nic = None; - let ret = KI.del_uci_var("network.lte"); + let ret = del_uci_var("network.lte"); return_codes.push(ret); } // LAN is a bridge and the lan bridge must always remain because things // like WiFi interfaces are attached to it. So we just remove the interface // from the list InterfaceMode::Lan => { - let list = KI.get_uci_var("network.lan.ifname")?; + let list = get_uci_var("network.lan.ifname")?; let new_list = list_remove(&list, ifname); - let ret = KI.set_uci_var("network.lan.ifname", &new_list); + let ret = set_uci_var("network.lan.ifname", &new_list); return_codes.push(ret); } // remove the section from the network and rita config, peer listener watches this setting @@ -283,7 +289,7 @@ pub fn ethernet_transform_mode( InterfaceMode::Mesh => { network.peer_interfaces.remove(ifname); - let ret = KI.del_uci_var(&filtered_ifname); + let ret = del_uci_var(&filtered_ifname); return_codes.push(ret); } InterfaceMode::Unknown => unimplemented!(), @@ -294,11 +300,11 @@ pub fn ethernet_transform_mode( InterfaceMode::Wan => { network.external_nic = Some(ifname.to_string()); - let ret = KI.set_uci_var("network.backhaul", "interface"); + let ret = set_uci_var("network.backhaul", "interface"); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.ifname", ifname); + let ret = set_uci_var("network.backhaul.ifname", ifname); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.proto", "dhcp"); + let ret = set_uci_var("network.backhaul.proto", "dhcp"); return_codes.push(ret); } InterfaceMode::StaticWan { @@ -308,45 +314,45 @@ pub fn ethernet_transform_mode( } => { network.external_nic = Some(ifname.to_string()); - let ret = KI.set_uci_var("network.backhaul", "interface"); + let ret = set_uci_var("network.backhaul", "interface"); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.ifname", ifname); + let ret = set_uci_var("network.backhaul.ifname", ifname); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.proto", "static"); + let ret = set_uci_var("network.backhaul.proto", "static"); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.netmask", &format!("{netmask}")); + let ret = set_uci_var("network.backhaul.netmask", &format!("{netmask}")); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.ipaddr", &format!("{ipaddr}")); + let ret = set_uci_var("network.backhaul.ipaddr", &format!("{ipaddr}")); return_codes.push(ret); - let ret = KI.set_uci_var("network.backhaul.gateway", &format!("{gateway}")); + let ret = set_uci_var("network.backhaul.gateway", &format!("{gateway}")); return_codes.push(ret); } InterfaceMode::LTE => { network.external_nic = Some(ifname.to_string()); - let ret = KI.set_uci_var("network.lte", "interface"); + let ret = set_uci_var("network.lte", "interface"); return_codes.push(ret); - let ret = KI.set_uci_var("network.lte.ifname", ifname); + let ret = set_uci_var("network.lte.ifname", ifname); return_codes.push(ret); - let ret = KI.set_uci_var("network.lte.proto", "dhcp"); + let ret = set_uci_var("network.lte.proto", "dhcp"); return_codes.push(ret); } // since we left lan mostly unmodified we just pop in the ifname InterfaceMode::Lan => { trace!("Converting interface to lan with ifname {:?}", ifname); - let ret = KI.get_uci_var("network.lan.ifname"); + let ret = get_uci_var("network.lan.ifname"); match ret { Ok(list) => { trace!("The existing LAN interfaces list is {:?}", list); let new_list = list_add(&list, ifname); trace!("Setting the new list {:?}", new_list); - let ret = KI.set_uci_var("network.lan.ifname", &new_list); + let ret = set_uci_var("network.lan.ifname", &new_list); return_codes.push(ret); } Err(e) => { if e.to_string().contains("Entry not found") { trace!("No LAN interfaces found, setting one now"); - let ret = KI.set_uci_var("network.lan.ifname", ifname); + let ret = set_uci_var("network.lan.ifname", ifname); return_codes.push(ret); } else { warn!("Trying to read lan ifname returned {:?}", e); @@ -358,11 +364,11 @@ pub fn ethernet_transform_mode( InterfaceMode::Mesh => { network.peer_interfaces.insert(ifname.to_string()); - let ret = KI.set_uci_var(&filtered_ifname, "interface"); + let ret = set_uci_var(&filtered_ifname, "interface"); return_codes.push(ret); - let ret = KI.set_uci_var(&format!("{filtered_ifname}.ifname"), ifname); + let ret = set_uci_var(&format!("{filtered_ifname}.ifname"), ifname); return_codes.push(ret); - let ret = KI.set_uci_var(&format!("{filtered_ifname}.proto"), "static"); + let ret = set_uci_var(&format!("{filtered_ifname}.proto"), "static"); return_codes.push(ret); } InterfaceMode::Unknown => unimplemented!(), @@ -377,7 +383,7 @@ pub fn ethernet_transform_mode( } let mut rita_client = settings::get_rita_client(); if !error_occured.is_empty() { - let res = KI.uci_revert("network"); + let res = uci_revert("network"); rita_client.network = old_network_settings; settings::set_rita_client(rita_client); //bail!("Error running UCI commands! Revert attempted: {:?}", res); @@ -394,8 +400,8 @@ pub fn ethernet_transform_mode( } } - KI.uci_commit("network")?; - KI.openwrt_reset_network()?; + uci_commit("network")?; + openwrt_reset_network()?; rita_client.network = network; settings::set_rita_client(rita_client); @@ -407,7 +413,7 @@ pub fn ethernet_transform_mode( trace!("Transforming ethernet"); // We edited disk contents, force global sync - KI.fs_sync()?; + fs_sync()?; Ok(()) } @@ -419,13 +425,13 @@ pub fn ethernet_transform_mode( /// to router wireless meshing'. fn wlan_toggle_get(uci_spec: &str) -> Result { - if !KI.is_openwrt() { + if !is_openwrt() { return Err(RitaCommonError::MiscStringError( "Not an OpenWRT device!".to_string(), )); } let bad_wireless = "Wireless config not correct"; - let current_state = KI.uci_show(Some(uci_spec))?; + let current_state = uci_show(Some(uci_spec))?; let current_state = match current_state.get(uci_spec) { Some(val) => val, None => return Err(RitaCommonError::MiscStringError(bad_wireless.to_string())), @@ -459,7 +465,7 @@ pub async fn wlan_mesh_get(_: HttpRequest) -> HttpResponse { } fn wlan_toggle_set(uci_spec: &str, enabled: bool) -> Result<(), RitaCommonError> { - if !KI.is_openwrt() { + if !is_openwrt() { return Err(RitaCommonError::MiscStringError( "Not an OpenWRT device!".to_string(), )); @@ -467,7 +473,7 @@ fn wlan_toggle_set(uci_spec: &str, enabled: bool) -> Result<(), RitaCommonError> let bad_wireless = "Wireless config not correct"; trace!("wlan toggle: uci_spec {}, enabled: {}", uci_spec, enabled,); - let current_state = KI.uci_show(Some(uci_spec))?; + let current_state = uci_show(Some(uci_spec))?; let current_state = match current_state.get(uci_spec) { Some(val) => val, None => return Err(RitaCommonError::MiscStringError(bad_wireless.to_string())), @@ -486,13 +492,13 @@ fn wlan_toggle_set(uci_spec: &str, enabled: bool) -> Result<(), RitaCommonError> // remember it's a 'disabled' toggle so we want to set it to zero to be 'enabled' let res = if enabled { - KI.set_uci_var(uci_spec, "0") + set_uci_var(uci_spec, "0") } else { - KI.set_uci_var(uci_spec, "1") + set_uci_var(uci_spec, "1") }; if let Err(e) = res { - let res_b = KI.uci_revert("wireless"); + let res_b = uci_revert("wireless"); if let Err(re) = res_b { return Err(RitaCommonError::InterfaceToggleError { main_error: vec![e], @@ -506,13 +512,13 @@ fn wlan_toggle_set(uci_spec: &str, enabled: bool) -> Result<(), RitaCommonError> } } - KI.uci_commit("wireless")?; - KI.openwrt_reset_wireless()?; + uci_commit("wireless")?; + openwrt_reset_wireless()?; // We edited disk contents, force global sync - KI.fs_sync()?; + fs_sync()?; - KI.run_command("reboot", &[])?; + run_command("reboot", &[])?; Ok(()) } diff --git a/rita_common/src/dashboard/logging.rs b/rita_common/src/dashboard/logging.rs index e1bd4b4b1..d8464a124 100644 --- a/rita_common/src/dashboard/logging.rs +++ b/rita_common/src/dashboard/logging.rs @@ -1,6 +1,7 @@ -use crate::KI; use actix_web_async::http::StatusCode; use actix_web_async::{web::Path, HttpRequest, HttpResponse}; +use althea_kernel_interface::is_openwrt::is_openwrt; +use althea_kernel_interface::run_command; use log::LevelFilter; pub async fn get_remote_logging(_req: HttpRequest) -> HttpResponse { @@ -25,8 +26,8 @@ pub async fn remote_logging(path: Path) -> HttpResponse { .json(format!("Failed to write config {e:?}")); } - if KI.is_openwrt() { - if let Err(e) = KI.run_command(service_path.as_str(), &["restart"]) { + if is_openwrt() { + if let Err(e) = run_command(service_path.as_str(), &["restart"]) { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR) .json(format!("Failed to restart service {e:?}")); } @@ -66,7 +67,7 @@ pub async fn remote_logging_level(path: Path) -> HttpResponse { .json(format!("Failed to write config {e:?}")); } - if let Err(e) = KI.run_command(service_path.as_str(), &["restart"]) { + if let Err(e) = run_command(service_path.as_str(), &["restart"]) { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR) .json(format!("Failed to restart service {e:?}")); } diff --git a/rita_common/src/dashboard/remote_access.rs b/rita_common/src/dashboard/remote_access.rs index eb18572a8..6186ebbec 100644 --- a/rita_common/src/dashboard/remote_access.rs +++ b/rita_common/src/dashboard/remote_access.rs @@ -1,16 +1,17 @@ use crate::RitaCommonError; use crate::DROPBEAR_CONFIG; -use crate::KI; use actix_web_async::http::StatusCode; use actix_web_async::web::Path; use actix_web_async::HttpRequest; use actix_web_async::HttpResponse; use althea_kernel_interface::file_io::get_lines; use althea_kernel_interface::file_io::write_out; +use althea_kernel_interface::is_openwrt::is_openwrt; +use althea_kernel_interface::run_command; static FIREWALL_CONFIG: &str = "/etc/config/firewall"; pub async fn get_remote_access_status(_req: HttpRequest) -> HttpResponse { - if !KI.is_openwrt() { + if !is_openwrt() { return HttpResponse::new(StatusCode::BAD_REQUEST); } HttpResponse::Ok().json(match check_dropbear_config() { @@ -25,7 +26,7 @@ pub async fn get_remote_access_status(_req: HttpRequest) -> HttpResponse { // the http responses at some point #[allow(dead_code)] pub fn get_remote_access_internal() -> Result { - if !KI.is_openwrt() { + if !is_openwrt() { return Err(RitaCommonError::ConversionError("Not Openwrt!".to_string())); } check_dropbear_config() @@ -81,7 +82,7 @@ pub fn set_remote_access_internal(remote_access: bool) -> Result<(), RitaCommonE lines.push(" option Interface 'lan'".to_string()); write_out(DROPBEAR_CONFIG, lines)?; - KI.run_command("/etc/init.d/dropbear", &["restart"])?; + run_command("/etc/init.d/dropbear", &["restart"])?; // this adds the updated rules to the firewall config, notice the versioning on the // firewall rules. The old ones will be left in place. @@ -114,7 +115,7 @@ pub fn set_remote_access_internal(remote_access: bool) -> Result<(), RitaCommonE } if needs_mesh_ssh || needs_wan_ssh { write_out(FIREWALL_CONFIG, firewall_lines)?; - KI.run_command("reboot", &[])?; + run_command("reboot", &[])?; Ok(()) } else { Ok(()) diff --git a/rita_common/src/dashboard/wifi.rs b/rita_common/src/dashboard/wifi.rs index bac052bd5..802536c31 100644 --- a/rita_common/src/dashboard/wifi.rs +++ b/rita_common/src/dashboard/wifi.rs @@ -1,10 +1,16 @@ //! These endpoints are used to modify mundane wireless settings use crate::dashboard::nickname::maybe_set_nickname; -use crate::{RitaCommonError, KI}; +use crate::RitaCommonError; use ::actix_web_async::http::StatusCode; use ::actix_web_async::web::Path; use ::actix_web_async::{web::Json, HttpRequest, HttpResponse}; +use althea_kernel_interface::exit_client_tunnel::create_client_nat_rules; +use althea_kernel_interface::fs_sync::fs_sync; +use althea_kernel_interface::manipulate_uci::{ + get_uci_var, openwrt_reset_wireless, set_uci_var, uci_commit, +}; +use althea_kernel_interface::openwrt_ubus::ubus_call; use althea_types::{ FromStr, WifiChannel, WifiDisabled, WifiPass, WifiSecurity, WifiSsid, WifiToken, }; @@ -206,12 +212,12 @@ pub fn set_ssid(wifi_ssid: &WifiSsid) -> Result<(), RitaCommonError> { let iface_name = wifi_ssid.radio.clone(); let ssid = wifi_ssid.ssid.clone(); let section_name = format!("default_{iface_name}"); - KI.set_uci_var(&format!("wireless.{section_name}.ssid"), &ssid)?; + set_uci_var(&format!("wireless.{section_name}.ssid"), &ssid)?; - KI.uci_commit("wireless")?; + uci_commit("wireless")?; // We edited disk contents, force global sync - KI.fs_sync()?; + fs_sync()?; // set the nickname with the first SSID change may fail // if the ssid is too long but don't block on that @@ -231,14 +237,14 @@ pub fn reset_wifi_pass() -> Result<(), RitaCommonError> { set_pass(&pass)?; } - KI.uci_commit("wireless")?; - KI.openwrt_reset_wireless()?; + uci_commit("wireless")?; + openwrt_reset_wireless()?; // We edited disk contents, force global sync - KI.fs_sync()?; + fs_sync()?; // we have invalidated the old nat rules, update them - KI.create_client_nat_rules()?; + create_client_nat_rules()?; Ok(()) } @@ -258,16 +264,15 @@ fn set_pass(wifi_pass: &WifiPass) -> Result<(), RitaCommonError> { let iface_name = wifi_pass.radio.clone(); let pass = wifi_pass.pass.clone(); let section_name = format!("default_{iface_name}"); - KI.set_uci_var(&format!("wireless.{section_name}.key"), &pass)?; + set_uci_var(&format!("wireless.{section_name}.key"), &pass)?; Ok(()) } fn set_channel(wifi_channel: &WifiChannel) -> Result<(), RitaCommonError> { - let current_channel: u16 = KI - .get_uci_var(&format!("wireless.{}.channel", wifi_channel.radio))? - .parse()?; - let channel_width = KI.get_uci_var(&format!("wireless.{}.htmode", wifi_channel.radio))?; + let current_channel: u16 = + get_uci_var(&format!("wireless.{}.channel", wifi_channel.radio))?.parse()?; + let channel_width = get_uci_var(&format!("wireless.{}.htmode", wifi_channel.radio))?; if let Err(e) = validate_channel( current_channel, @@ -279,7 +284,7 @@ fn set_channel(wifi_channel: &WifiChannel) -> Result<(), RitaCommonError> { return Err(e.into()); } - KI.set_uci_var( + set_uci_var( &format!("wireless.{}.channel", wifi_channel.radio), &wifi_channel.channel.to_string(), )?; @@ -294,7 +299,7 @@ fn set_security(wifi_security: &WifiSecurity) -> Result<(), RitaCommonError> { // think radio0, radio1 let iface_name = wifi_security.radio.clone(); let section_name = format!("default_{iface_name}"); - KI.set_uci_var( + set_uci_var( &format!("wireless.{section_name}.encryption"), &parsed.as_config_value(), )?; @@ -310,13 +315,13 @@ fn set_security(wifi_security: &WifiSecurity) -> Result<(), RitaCommonError> { /// Disables the wifi on the specified radio fn set_disabled(wifi_disabled: &WifiDisabled) -> Result<(), RitaCommonError> { let current_disabled: bool = - KI.get_uci_var(&format!("wireless.{}.disabled", wifi_disabled.radio))? == "1"; + get_uci_var(&format!("wireless.{}.disabled", wifi_disabled.radio))? == "1"; if current_disabled == wifi_disabled.disabled { return Ok(()); } - KI.set_uci_var( + set_uci_var( &format!("wireless.{}.disabled", wifi_disabled.radio), if wifi_disabled.disabled { "1" } else { "0" }, )?; @@ -392,25 +397,25 @@ pub fn set_wifi_multi_internal(wifi_changes: Vec) -> HttpResponse { }; } - if let Err(e) = KI.uci_commit("wireless") { + if let Err(e) = uci_commit("wireless") { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(ErrorJsonResponse { error: format!("{e}"), }); } - if let Err(e) = KI.openwrt_reset_wireless() { + if let Err(e) = openwrt_reset_wireless() { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(ErrorJsonResponse { error: format!("{e}"), }); } // We edited disk contents, force global sync - if let Err(e) = KI.fs_sync() { + if let Err(e) = fs_sync() { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(ErrorJsonResponse { error: format!("{e}"), }); } // we have invalidated the old nat rules, update them - if let Err(e) = KI.create_client_nat_rules() { + if let Err(e) = create_client_nat_rules() { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(ErrorJsonResponse { error: format!("{e}"), }); @@ -521,7 +526,7 @@ pub async fn get_allowed_wifi_channels(radio: Path) -> HttpResponse { debug!("/wifi_settings/get_channels hit with {:?}", radio); let radio = radio.into_inner(); - let current_channel: u16 = match KI.get_uci_var(&format!("wireless.{radio}.channel")) { + let current_channel: u16 = match get_uci_var(&format!("wireless.{radio}.channel")) { Ok(uci) => match uci.parse() { Ok(a) => a, Err(e) => { @@ -532,7 +537,7 @@ pub async fn get_allowed_wifi_channels(radio: Path) -> HttpResponse { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(format!("{e}")); } }; - let five_channel_width = match KI.get_uci_var(&format!("wireless.{radio}.htmode")) { + let five_channel_width = match get_uci_var(&format!("wireless.{radio}.htmode")) { Ok(a) => a, Err(e) => { return HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).json(format!("{e}")); @@ -619,7 +624,7 @@ pub async fn get_wifi_config(_req: HttpRequest) -> HttpResponse { pub fn get_wifi_config_internal() -> Result, RitaCommonError> { let mut interfaces = Vec::new(); let mut devices = HashMap::new(); - let config = KI.ubus_call("uci", "get", "{ \"config\": \"wireless\"}")?; + let config = ubus_call("uci", "get", "{ \"config\": \"wireless\"}")?; let val: Value = serde_json::from_str(&config)?; let items = match val["values"].as_object() { Some(i) => i, diff --git a/rita_common/src/debt_keeper/mod.rs b/rita_common/src/debt_keeper/mod.rs index 71c17d8ca..1478fa0e9 100755 --- a/rita_common/src/debt_keeper/mod.rs +++ b/rita_common/src/debt_keeper/mod.rs @@ -15,7 +15,7 @@ use crate::tunnel_manager::tm_tunnel_state_change; use crate::tunnel_manager::TunnelAction; use crate::tunnel_manager::TunnelChange; use crate::RitaCommonError; -use crate::KI; +use althea_kernel_interface::netns::check_integration_test_netns; use althea_types::Denom; use althea_types::Identity; use althea_types::UnpublishedPaymentTx; @@ -60,7 +60,7 @@ pub fn wei_denom() -> Denom { /// Gets DebtKeeper copy from the static ref, or default if no value has been set pub fn get_debt_keeper() -> DebtKeeper { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); DEBT_DATA .read() .unwrap() @@ -74,7 +74,7 @@ pub fn get_debt_keeper() -> DebtKeeper { /// the lock will be held until you drop the return value, this lets the caller abstract the namespace handling /// but still hold the lock in the local thread to prevent parallel modification fn get_debt_keeper_write_ref(input: &mut HashMap) -> &mut DebtKeeper { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); input.entry(netns).or_default(); input.get_mut(&netns).unwrap() } diff --git a/rita_common/src/lib.rs b/rita_common/src/lib.rs index 2dfddde7d..5bc2a76e2 100644 --- a/rita_common/src/lib.rs +++ b/rita_common/src/lib.rs @@ -8,13 +8,6 @@ extern crate lazy_static; extern crate serde_derive; extern crate arrayvec; -use althea_kernel_interface::KernelInterface; - -use althea_kernel_interface::LinuxCommandRunner; - -lazy_static! { - pub static ref KI: Box = Box::new(LinuxCommandRunner {}); -} pub static DROPBEAR_CONFIG: &str = "/etc/config/dropbear"; pub static DROPBEAR_AUTHORIZED_KEYS: &str = "/etc/dropbear/authorized_keys"; diff --git a/rita_common/src/payment_validator/mod.rs b/rita_common/src/payment_validator/mod.rs index 3684c71ed..768945673 100644 --- a/rita_common/src/payment_validator/mod.rs +++ b/rita_common/src/payment_validator/mod.rs @@ -14,7 +14,7 @@ use crate::rita_loop::fast_loop::FAST_LOOP_TIMEOUT; use crate::rita_loop::get_web3_server; use crate::usage_tracker::update_payments; use crate::RitaCommonError; -use crate::KI; +use althea_kernel_interface::netns::check_integration_test_netns; use althea_proto::althea::microtx::v1::MsgMicrotx; use althea_types::Denom; use althea_types::Identity; @@ -82,7 +82,7 @@ lazy_static! { /// Adds an incoming transaction to the global incoming transaction queue abstracts netns handling /// to ensure that multiple instances can run in the same process without interfering with each other pub fn add_to_incoming_transaction_queue(tx: ToValidate) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); INCOMING_TRANSACTIONS.push((netns, tx)); } @@ -90,7 +90,7 @@ pub fn add_to_incoming_transaction_queue(tx: ToValidate) { /// the queue while doing so. This abstracts netns handling to ensure that multiple instances /// can run in the same process without interfering with each other pub fn get_incoming_transaction_queue() -> Vec { - let our_netns = KI.check_integration_test_netns(); + let our_netns = check_integration_test_netns(); // this is a hack, in order to avoid any locks at all we iterate // over the entire queue and only take the items that are for our netns // it helps that this overhead will only ever occur in the integration tests diff --git a/rita_common/src/peer_listener/mod.rs b/rita_common/src/peer_listener/mod.rs index 74891b477..5e764c01c 100644 --- a/rita_common/src/peer_listener/mod.rs +++ b/rita_common/src/peer_listener/mod.rs @@ -15,7 +15,8 @@ use crate::peer_listener::structs::PeerListener; use crate::tm_identity_callback; use crate::IdentityCallback; use crate::RitaCommonError; -use crate::KI; +use althea_kernel_interface::interface_tools::get_ifindex; +use althea_kernel_interface::link_local_tools::get_link_local_device_ip; use althea_types::LocalIdentity; use std::collections::HashMap; use std::net::{IpAddr, Ipv6Addr, SocketAddr, SocketAddrV6, UdpSocket}; @@ -108,11 +109,11 @@ impl ListenInterface { let disc_ip = network.discovery_ip; trace!("Binding to {:?} for ListenInterface", ifname); // Lookup interface link local ip - let link_ip = KI.get_link_local_device_ip(ifname)?; + let link_ip = get_link_local_device_ip(ifname)?; trace!("Link ip is {:?}", link_ip); // Lookup interface index - let iface_index: u32 = KI.get_ifindex(ifname).unwrap_or(0) as u32; + let iface_index: u32 = get_ifindex(ifname).unwrap_or(0) as u32; // Bond to multicast discovery address on each listen port let multicast_socketaddr = SocketAddrV6::new(disc_ip, port, 0, iface_index); let multicast_socket = UdpSocket::bind(multicast_socketaddr)?; diff --git a/rita_common/src/rita_loop/fast_loop.rs b/rita_common/src/rita_loop/fast_loop.rs index 388d8c1d5..8fd79164a 100644 --- a/rita_common/src/rita_loop/fast_loop.rs +++ b/rita_common/src/rita_loop/fast_loop.rs @@ -9,8 +9,9 @@ use crate::peer_listener::structs::PeerListener; use crate::traffic_watcher::watch; use crate::tunnel_manager::contact_peers::tm_contact_peers; use crate::tunnel_manager::tm_get_neighbors; -use crate::KI; use actix_async::System as AsyncSystem; +use althea_kernel_interface::is_openwrt::is_openwrt; +use althea_kernel_interface::run_command; use babel_monitor::open_babel_stream; use babel_monitor::parse_neighs; use babel_monitor::parse_routes; @@ -124,8 +125,8 @@ pub fn start_rita_fast_loop() { if Instant::now() - last_restart < Duration::from_secs(60) { error!("Restarting too quickly, rebooting instead!"); // only reboot if we are on openwrt, otherwise we are probably on a datacenter server rebooting that is a bad idea - if KI.is_openwrt() { - let _res = KI.run_command("reboot", &[]); + if is_openwrt() { + let _res = run_command("reboot", &[]); } } last_restart = Instant::now(); @@ -188,8 +189,8 @@ pub fn peer_discovery_loop() { if Instant::now() - last_restart < Duration::from_secs(60) { error!("Restarting too quickly, rebooting instead!"); // only reboot if we are on openwrt, otherwise we are probably on a datacenter server rebooting that is a bad idea - if KI.is_openwrt() { - let _res = KI.run_command("reboot", &[]); + if is_openwrt() { + let _res = run_command("reboot", &[]); } } last_restart = Instant::now(); diff --git a/rita_common/src/rita_loop/slow_loop.rs b/rita_common/src/rita_loop/slow_loop.rs index b45d134c9..52840f3f4 100644 --- a/rita_common/src/rita_loop/slow_loop.rs +++ b/rita_common/src/rita_loop/slow_loop.rs @@ -2,8 +2,10 @@ use crate::handle_shaping; use crate::simulated_txfee_manager::tick_simulated_tx; use crate::token_bridge::tick_token_bridge; use crate::tunnel_manager::tm_common_slow_loop_helper; -use crate::KI; use actix_async::System as AsyncSystem; +use althea_kernel_interface::babel::restart_babel; +use althea_kernel_interface::is_openwrt::is_openwrt; +use althea_kernel_interface::run_command; use babel_monitor::open_babel_stream; use babel_monitor::parse_interfaces; use babel_monitor::set_local_fee; @@ -89,7 +91,7 @@ pub fn start_rita_slow_loop() { error!("We have not successfully talked to babel in {} loop iterations, restarting babel", num_babel_failures); // we restart babel here and then rely on the tm_monitor_check function to re-attach the tunnels in the next loop // iteration - KI.restart_babel(); + restart_babel(); } thread::sleep(SLOW_LOOP_SPEED); @@ -104,8 +106,8 @@ pub fn start_rita_slow_loop() { if Instant::now() - last_restart < Duration::from_secs(120) { error!("Restarting too quickly, rebooting instead!"); // only reboot if we are on openwrt, otherwise we are probably on a datacenter server rebooting that is a bad idea - if KI.is_openwrt() { - let _res = KI.run_command("reboot", &[]); + if is_openwrt() { + let _res = run_command("reboot", &[]); } } last_restart = Instant::now(); diff --git a/rita_common/src/simulated_txfee_manager/mod.rs b/rita_common/src/simulated_txfee_manager/mod.rs index 323a93b30..e21ab8e2d 100644 --- a/rita_common/src/simulated_txfee_manager/mod.rs +++ b/rita_common/src/simulated_txfee_manager/mod.rs @@ -4,7 +4,7 @@ use crate::blockchain_oracle::get_pay_thresh; use crate::payment_controller::TRANSACTION_SUBMISSION_TIMEOUT; use crate::rita_loop::get_web3_server; use crate::usage_tracker::update_payments; -use crate::KI; +use althea_kernel_interface::netns::check_integration_test_netns; use althea_types::Identity; use althea_types::PaymentTx; use num256::Uint256; @@ -21,7 +21,7 @@ lazy_static! { /// Gets Amount owed copy from the static ref, or default if no value has been set pub fn get_amount_owed() -> Uint256 { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); AMOUNT_OWED .read() .unwrap() @@ -35,7 +35,7 @@ pub fn get_amount_owed() -> Uint256 { /// the lock will be held until you drop the return value, this lets the caller abstract the namespace handling /// but still hold the lock in the local thread to prevent parallel modification pub fn get_amount_owed_write_ref(input: &mut HashMap) -> &mut Uint256 { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); input.entry(netns).or_insert_with(Uint256::zero); input.get_mut(&netns).unwrap() } diff --git a/rita_common/src/traffic_watcher/mod.rs b/rita_common/src/traffic_watcher/mod.rs index cfc3091e0..9c288c71f 100644 --- a/rita_common/src/traffic_watcher/mod.rs +++ b/rita_common/src/traffic_watcher/mod.rs @@ -9,7 +9,8 @@ use crate::usage_tracker::structs::UsageType; use crate::usage_tracker::update_usage_data; use crate::usage_tracker::UpdateUsage; use crate::RitaCommonError; -use crate::KI; +use althea_kernel_interface::counter::init_counter; +use althea_kernel_interface::counter::read_counters; use althea_kernel_interface::open_tunnel::is_link_local; use althea_kernel_interface::FilterTarget; use althea_types::Identity; @@ -35,14 +36,10 @@ impl Watch { } pub fn init_traffic_watcher() { - KI.init_counter(&FilterTarget::Input) - .expect("Is ipset installed?"); - KI.init_counter(&FilterTarget::Output) - .expect("Is ipset installed?"); - KI.init_counter(&FilterTarget::ForwardInput) - .expect("Is ipset installed?"); - KI.init_counter(&FilterTarget::ForwardOutput) - .expect("Is ipset installed?"); + init_counter(&FilterTarget::Input).expect("Is ipset installed?"); + init_counter(&FilterTarget::Output).expect("Is ipset installed?"); + init_counter(&FilterTarget::ForwardInput).expect("Is ipset installed?"); + init_counter(&FilterTarget::ForwardOutput).expect("Is ipset installed?"); info!("Traffic Watcher started"); } @@ -110,7 +107,7 @@ pub fn get_babel_info(routes: Vec) -> Result<(HashMap, u32) pub fn get_input_counters() -> Result, RitaCommonError> { let mut total_input_counters = HashMap::new(); trace!("Getting input counters"); - let input_counters = match KI.read_counters(&FilterTarget::Input) { + let input_counters = match read_counters(&FilterTarget::Input) { Ok(res) => res, Err(e) => { warn!( @@ -122,7 +119,7 @@ pub fn get_input_counters() -> Result, RitaCommon }; trace!("Got input counters: {:?}", input_counters); trace!("Getting fwd counters"); - let fwd_input_counters = match KI.read_counters(&FilterTarget::ForwardInput) { + let fwd_input_counters = match read_counters(&FilterTarget::ForwardInput) { Ok(res) => res, Err(e) => { warn!( @@ -164,7 +161,7 @@ pub fn get_input_counters() -> Result, RitaCommon pub fn get_output_counters() -> Result, RitaCommonError> { let mut total_output_counters = HashMap::new(); trace!("Getting ouput counters"); - let output_counters = match KI.read_counters(&FilterTarget::Output) { + let output_counters = match read_counters(&FilterTarget::Output) { Ok(res) => res, Err(e) => { warn!( @@ -176,7 +173,7 @@ pub fn get_output_counters() -> Result, RitaCommo }; trace!("Got output counters: {:?}", output_counters); - let fwd_output_counters = match KI.read_counters(&FilterTarget::ForwardOutput) { + let fwd_output_counters = match read_counters(&FilterTarget::ForwardOutput) { Ok(res) => res, Err(e) => { warn!( diff --git a/rita_common/src/tunnel_manager/contact_peers.rs b/rita_common/src/tunnel_manager/contact_peers.rs index 02125de8f..047234328 100644 --- a/rita_common/src/tunnel_manager/contact_peers.rs +++ b/rita_common/src/tunnel_manager/contact_peers.rs @@ -13,7 +13,7 @@ use crate::tm_identity_callback; use crate::tunnel_manager::get_tunnel_manager; use crate::IdentityCallback; use crate::RitaCommonError; -use crate::KI; +use althea_kernel_interface::ip_route::manual_peers_route; use althea_types::LocalIdentity; use futures::future::join_all; use std::net::ToSocketAddrs; @@ -73,7 +73,7 @@ pub async fn tm_neighbor_inquiry_manual_peer(peer: Peer) -> Result<(), RitaCommo trace!("TunnelManager neigh inquiry for {:?}", peer); let our_port = get_tunnel_manager().get_next_available_port()?; let mut settings = settings::get_rita_common(); - let changed = KI.manual_peers_route( + let changed = manual_peers_route( &peer.contact_socket.ip(), &mut settings.network.last_default_route, )?; diff --git a/rita_common/src/tunnel_manager/gc.rs b/rita_common/src/tunnel_manager/gc.rs index fcf5f58b4..38015f052 100644 --- a/rita_common/src/tunnel_manager/gc.rs +++ b/rita_common/src/tunnel_manager/gc.rs @@ -1,5 +1,5 @@ use super::{Tunnel, TunnelManager}; -use crate::KI; +use althea_kernel_interface::setup_wg_if::get_last_handshake_time; use althea_types::Identity; use babel_monitor::structs::Interface; use std::time::Duration; @@ -193,7 +193,7 @@ pub fn insert_into_tunnel_list(input: &Tunnel, tunnels_list: &mut HashMap bool { - let res = KI.get_last_handshake_time(ifname); + let res = get_last_handshake_time(ifname); match res { Ok(handshakes) => { for (_key, time) in handshakes { diff --git a/rita_common/src/tunnel_manager/mod.rs b/rita_common/src/tunnel_manager/mod.rs index 2f8ab367d..fcb087f01 100644 --- a/rita_common/src/tunnel_manager/mod.rs +++ b/rita_common/src/tunnel_manager/mod.rs @@ -18,10 +18,17 @@ use crate::tunnel_manager::error::TunnelManagerError; use crate::RitaCommonError; use crate::Shaper; use crate::FAST_LOOP_TIMEOUT; -use crate::KI; use crate::TUNNEL_HANDSHAKE_TIMEOUT; use crate::TUNNEL_TIMEOUT; +use althea_kernel_interface::interface_tools::del_interface; +use althea_kernel_interface::netns::check_integration_test_netns; +use althea_kernel_interface::open_tunnel::open_tunnel; use althea_kernel_interface::open_tunnel::TunnelOpenArgs; +use althea_kernel_interface::setup_wg_if::create_blank_wg_numbered_wg_interface; +use althea_kernel_interface::traffic_control::has_limit; +use althea_kernel_interface::traffic_control::set_classless_limit; +use althea_kernel_interface::traffic_control::set_codel_shaping; +use althea_kernel_interface::udp_socket_table::used_ports; use althea_types::Identity; use althea_types::LocalIdentity; use babel_monitor::monitor; @@ -47,7 +54,7 @@ lazy_static! { /// Gets TunnelManager copy from the static ref, or default if no value has been set pub fn get_tunnel_manager() -> TunnelManager { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); TUNNEL_MANAGER .read() .unwrap() @@ -61,7 +68,7 @@ pub fn get_tunnel_manager() -> TunnelManager { /// the lock will be held until you drop the return value, this lets the caller abstract the namespace handling /// but still hold the lock in the local thread to prevent parallel modification pub fn get_tunnel_manager_write_ref(input: &mut HashMap) -> &mut TunnelManager { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); input.entry(netns).or_default(); input.get_mut(&netns).unwrap() } @@ -163,7 +170,7 @@ impl Tunnel { } }; // after this step we have created a blank wg interface that we should clean up if we fail - let iface_name = KI.create_blank_wg_numbered_wg_interface()?; + let iface_name = create_blank_wg_numbered_wg_interface()?; let args = TunnelOpenArgs { interface: iface_name.clone(), @@ -177,14 +184,14 @@ impl Tunnel { settings_default_route: &mut network.last_default_route, }; - if let Err(e) = KI.open_tunnel(args) { + if let Err(e) = open_tunnel(args) { error!("Failed open tunnel! {:?}", e); // cleanup after our failed attempt - KI.del_interface(&iface_name)?; + del_interface(&iface_name)?; return Err(e.into()); } // a failure here isn't fatal, we just won't have traffic shaping - if let Err(e) = KI.set_codel_shaping(&iface_name, speed_limit) { + if let Err(e) = set_codel_shaping(&iface_name, speed_limit) { error!("Failed to setup codel shaping on tunnel! {:?}", e); } @@ -206,7 +213,7 @@ impl Tunnel { if let Err(e) = t.monitor() { error!("Failed to monitor tunnel! {:?}", e); // cleanup after our failed attempt - KI.del_interface(&t.iface_name)?; + del_interface(&t.iface_name)?; return Err(e.into()); } @@ -242,7 +249,7 @@ impl Tunnel { // We must wait until we have flushed the interface before deleting it // otherwise we will experience this error // https://github.com/sudomesh/bugs/issues/24 - if let Err(e) = KI.del_interface(&tunnel.iface_name) { + if let Err(e) = del_interface(&tunnel.iface_name) { error!("Failed to delete wg interface! {:?}", e); return Err(e.into()); } @@ -351,7 +358,7 @@ impl TunnelManager { /// Gets a port off of the internal port list after checking that said port is free /// with the operating system. fn get_next_available_port(&self) -> Result { - let udp_table = KI.used_ports()?; + let udp_table = used_ports()?; let used_ports = self.get_all_used_ports(); let start = settings::get_rita_common().network.wg_start_port; @@ -610,15 +617,15 @@ fn tunnel_bw_limit_update(tunnels: &[Tunnel]) -> Result<(), RitaCommonError> { for tunnel in tunnels { let payment_state = &tunnel.payment_state; let iface_name = &tunnel.iface_name; - let has_limit = KI.has_limit(iface_name)?; + let has_limit = has_limit(iface_name)?; if *payment_state == PaymentState::Overdue && !has_limit && !potential_payment_issues_detected() { - KI.set_classless_limit(iface_name, bw_per_iface)?; + set_classless_limit(iface_name, bw_per_iface)?; } else if *payment_state == PaymentState::Paid && has_limit { - KI.set_codel_shaping(iface_name, None)?; + set_codel_shaping(iface_name, None)?; } } Ok(()) diff --git a/rita_common/src/tunnel_manager/shaping.rs b/rita_common/src/tunnel_manager/shaping.rs index b0fddbe70..645d208c9 100644 --- a/rita_common/src/tunnel_manager/shaping.rs +++ b/rita_common/src/tunnel_manager/shaping.rs @@ -1,7 +1,8 @@ +use althea_kernel_interface::traffic_control::set_codel_shaping; + use super::get_tunnel_manager_write_ref; use super::TunnelManager; use super::TUNNEL_MANAGER; -use crate::KI; /// contains the state for the shaper #[derive(Debug, Default, Clone)] @@ -123,7 +124,7 @@ impl TunnelManager { /// tiny little helper function for GotBloat() limit is in mbps fn set_shaping_or_error(iface: &str, limit: Option) { - if let Err(e) = KI.set_codel_shaping(iface, limit) { + if let Err(e) = set_codel_shaping(iface, limit) { error!("Failed to shape tunnel for bloat! {}", e); } } diff --git a/rita_exit/src/database/geoip.rs b/rita_exit/src/database/geoip.rs index 21ec4670b..a8b7e9aab 100644 --- a/rita_exit/src/database/geoip.rs +++ b/rita_exit/src/database/geoip.rs @@ -1,9 +1,9 @@ +use althea_kernel_interface::interface_tools::get_wg_remote_ip; use althea_types::regions::Regions; use babel_monitor::open_babel_stream; use babel_monitor::parse_routes; use ipnetwork::IpNetwork; use rita_common::utils::ip_increment::is_unicast_link_local; -use rita_common::KI; use std::collections::HashMap; use std::net::IpAddr; use std::time::Duration; @@ -34,7 +34,7 @@ pub fn get_gateway_ip_single(mesh_ip: IpAddr) -> Result Ok(match KI.get_wg_remote_ip(&route.iface) { + Some(route) => Ok(match get_wg_remote_ip(&route.iface) { Ok(a) => a, Err(e) => return Err(Box::new(e.into())), }), @@ -91,7 +91,7 @@ pub fn get_gateway_ip_bulk( gateway_ip: *remote_ip, }); } else { - match KI.get_wg_remote_ip(&route.iface) { + match get_wg_remote_ip(&route.iface) { Ok(remote_ip) => { remote_ip_cache .insert(route.iface.clone(), remote_ip); diff --git a/rita_exit/src/database/mod.rs b/rita_exit/src/database/mod.rs index 3da32b9fa..d1c83cc29 100644 --- a/rita_exit/src/database/mod.rs +++ b/rita_exit/src/database/mod.rs @@ -14,6 +14,16 @@ use crate::rita_loop::EXIT_LOOP_TIMEOUT; use crate::rita_loop::LEGACY_INTERFACE; use crate::IpAssignmentMap; use crate::RitaExitError; +use althea_kernel_interface::exit_server_tunnel::set_exit_wg_config; +use althea_kernel_interface::exit_server_tunnel::setup_individual_client_routes; +use althea_kernel_interface::exit_server_tunnel::teardown_individual_client_routes; +use althea_kernel_interface::setup_wg_if::get_last_active_handshake_time; +use althea_kernel_interface::traffic_control::create_flow_by_ip; +use althea_kernel_interface::traffic_control::create_flow_by_ipv6; +use althea_kernel_interface::traffic_control::delete_class; +use althea_kernel_interface::traffic_control::has_class; +use althea_kernel_interface::traffic_control::has_flow; +use althea_kernel_interface::traffic_control::set_class_limit; use althea_kernel_interface::ExitClient; use althea_types::regions::Regions; use althea_types::Identity; @@ -25,7 +35,6 @@ use rita_client_registration::ExitSignupReturn; use rita_common::blockchain_oracle::calculate_close_thresh; use rita_common::debt_keeper::get_debts_list; use rita_common::debt_keeper::DebtAction; -use rita_common::KI; use settings::get_rita_exit; use std::collections::HashMap; use std::collections::HashSet; @@ -366,7 +375,7 @@ pub fn setup_clients( { info!("Setting up configs for wg_exit and wg_exit_v2"); // setup all the tunnels - let exit_status = KI.set_exit_wg_config( + let exit_status = set_exit_wg_config( &wg_clients, settings::get_rita_exit().exit_network.wg_tunnel_port, &settings::get_rita_exit().network.wg_private_key_path, @@ -386,7 +395,7 @@ pub fn setup_clients( } // Setup new tunnels - let exit_status_new = KI.set_exit_wg_config( + let exit_status_new = set_exit_wg_config( &wg_clients, settings::get_rita_exit().exit_network.wg_v2_tunnel_port, &settings::get_rita_exit().network.wg_private_key_path, @@ -419,16 +428,16 @@ pub fn setup_clients( // 2.) From these timestamps, determine if client is wg exit v1 or v2 // 3.) Compare this to our datastore of previous clients we set up routes for // 4.) Set up routes for v2 or v1 based on this - let new_wg_exit_clients_timestamps: HashMap = KI - .get_last_active_handshake_time(EXIT_INTERFACE) - .expect("There should be a new wg_exit interface") - .into_iter() - .collect(); - let wg_exit_clients_timestamps: HashMap = KI - .get_last_active_handshake_time(LEGACY_INTERFACE) - .expect("There should be a wg_exit interface") - .into_iter() - .collect(); + let new_wg_exit_clients_timestamps: HashMap = + get_last_active_handshake_time(EXIT_INTERFACE) + .expect("There should be a new wg_exit interface") + .into_iter() + .collect(); + let wg_exit_clients_timestamps: HashMap = + get_last_active_handshake_time(LEGACY_INTERFACE) + .expect("There should be a wg_exit interface") + .into_iter() + .collect(); let client_list_for_setup: Vec = key_to_client_map .clone() @@ -464,7 +473,7 @@ pub fn setup_clients( // all traffic will go over wg_exit_v2 for c_key in changed_clients_return.new_v1 { if let Some(c) = key_to_client_map.get(&c_key) { - KI.setup_individual_client_routes( + setup_individual_client_routes( match get_client_internal_ip( *c, get_rita_exit().exit_network.netmask, @@ -486,7 +495,7 @@ pub fn setup_clients( } for c_key in changed_clients_return.new_v2 { if let Some(c) = key_to_client_map.get(&c_key) { - KI.teardown_individual_client_routes( + teardown_individual_client_routes( match get_client_internal_ip( *c, get_rita_exit().exit_network.netmask, @@ -641,8 +650,8 @@ pub fn enforce_exit_clients( // setup flows this allows us to classify traffic we then limit the class, we delete the class as part of unenforcment but it's difficult to delete the flows // so a user who has been enforced and unenforced while the exit has been online may already have them setup let flow_setup_required = match ( - KI.has_flow(ip, EXIT_INTERFACE), - KI.has_flow(ip, LEGACY_INTERFACE), + has_flow(ip, EXIT_INTERFACE), + has_flow(ip, LEGACY_INTERFACE), ) { (Ok(true), Ok(true)) | (Ok(true), Ok(false)) @@ -661,10 +670,10 @@ pub fn enforce_exit_clients( }; if flow_setup_required { // create ipv4 and ipv6 flows, which are used to classify traffic, we can then limit the class specifically - if let Err(e) = KI.create_flow_by_ip(LEGACY_INTERFACE, ip) { + if let Err(e) = create_flow_by_ip(LEGACY_INTERFACE, ip) { error!("Failed to setup flow for wg_exit {:?}", e); } - if let Err(e) = KI.create_flow_by_ip(EXIT_INTERFACE, ip) { + if let Err(e) = create_flow_by_ip(EXIT_INTERFACE, ip) { error!("Failed to setup flow for wg_exit_v2 {:?}", e); } // gets the client ipv6 flow for this exit specifically @@ -677,7 +686,7 @@ pub fn enforce_exit_clients( ); if let Ok(Some(client_ipv6)) = client_ipv6 { if let Err(e) = - KI.create_flow_by_ipv6(EXIT_INTERFACE, client_ipv6, ip) + create_flow_by_ipv6(EXIT_INTERFACE, client_ipv6, ip) { error!("Failed to setup ipv6 flow for wg_exit_v2 {:?}", e); } @@ -688,7 +697,7 @@ pub fn enforce_exit_clients( ) } - if let Err(e) = KI.set_class_limit( + if let Err(e) = set_class_limit( LEGACY_INTERFACE, free_tier_limit, free_tier_limit, @@ -696,7 +705,7 @@ pub fn enforce_exit_clients( ) { error!("Unable to setup enforcement class on wg_exit: {:?}", e); } - if let Err(e) = KI.set_class_limit( + if let Err(e) = set_class_limit( EXIT_INTERFACE, free_tier_limit, free_tier_limit, @@ -706,8 +715,8 @@ pub fn enforce_exit_clients( } } else { let action_required = match ( - KI.has_class(ip, LEGACY_INTERFACE), - KI.has_class(ip, EXIT_INTERFACE), + has_class(ip, LEGACY_INTERFACE), + has_class(ip, EXIT_INTERFACE), ) { (Ok(a), Ok(b)) => a | b, (Ok(a), Err(_)) => a, @@ -721,11 +730,11 @@ pub fn enforce_exit_clients( // Delete exisiting enforcement class, users who are not enforced are unclassifed becuase // leaving the class in place reduces their speeds. info!("Deleting enforcement classes for {}", client.public_key); - if let Err(e) = KI.delete_class(LEGACY_INTERFACE, ip) { + if let Err(e) = delete_class(LEGACY_INTERFACE, ip) { error!("Unable to delete class on wg_exit, is {} still enforced when they shouldnt be? {:?}", ip, e); } - if let Err(e) = KI.delete_class(EXIT_INTERFACE, ip) { + if let Err(e) = delete_class(EXIT_INTERFACE, ip) { error!("Unable to delete class on wg_exit_v2, is {} still enforced when they shouldnt be? {:?}", ip, e); } } diff --git a/rita_exit/src/operator_update/mod.rs b/rita_exit/src/operator_update/mod.rs index d8671f8ef..3c04c64d7 100644 --- a/rita_exit/src/operator_update/mod.rs +++ b/rita_exit/src/operator_update/mod.rs @@ -1,7 +1,7 @@ //! This module is responsible for checking in with the operator server and getting updated local settings pub mod update_loop; +use althea_kernel_interface::setup_wg_if::get_wg_exit_clients_online; use althea_types::OperatorExitCheckinMessage; -use rita_common::KI; use std::time::{Duration, Instant}; use crate::rita_loop::EXIT_INTERFACE; @@ -47,7 +47,7 @@ pub async fn operator_update(rita_started: Instant) { id, exit_uptime: rita_started.elapsed(), // Since this checkin works only from b20, we only need to look on wg_exit_v2 - users_online: KI.get_wg_exit_clients_online(EXIT_INTERFACE).ok(), + users_online: get_wg_exit_clients_online(EXIT_INTERFACE).ok(), }) .await; match response { diff --git a/rita_exit/src/rita_loop/mod.rs b/rita_exit/src/rita_loop/mod.rs index 032fa6136..339149dbe 100644 --- a/rita_exit/src/rita_loop/mod.rs +++ b/rita_exit/src/rita_loop/mod.rs @@ -17,6 +17,8 @@ use crate::network_endpoints::*; use crate::traffic_watcher::watch_exit_traffic; use actix_async::System as AsyncSystem; use actix_web_async::{web, App, HttpServer}; +use althea_kernel_interface::exit_server_tunnel::{one_time_exit_setup, setup_nat}; +use althea_kernel_interface::setup_wg_if::create_blank_wg_interface; use althea_kernel_interface::wg_iface_counter::WgUsage; use althea_kernel_interface::ExitClient; use althea_types::{Identity, WgKey}; @@ -24,7 +26,6 @@ use babel_monitor::{open_babel_stream, parse_routes}; use rita_client_registration::client_db::get_all_regsitered_clients; use rita_common::debt_keeper::DebtAction; use rita_common::rita_loop::get_web3_server; -use rita_common::KI; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, RwLock}; use std::thread; @@ -278,11 +279,11 @@ fn check_regions(start: Instant, clients_list: Vec) -> Option) { } info!("Total exit income of {:?} Wei this round", total_income); - match KI.get_wg_exit_clients_online(LEGACY_INTERFACE) { + match get_wg_exit_clients_online(LEGACY_INTERFACE) { Ok(users) => info!("Total of {} wg_exit users online", users), Err(e) => warn!("Getting clients failed with {:?}", e), } - match KI.get_wg_exit_clients_online(EXIT_INTERFACE) { + match get_wg_exit_clients_online(EXIT_INTERFACE) { Ok(users) => info!("Total of {} wg_exit_v2 users online", users), Err(e) => warn!("Getting clients failed with {:?}", e), } @@ -180,7 +181,7 @@ pub fn watch_exit_traffic( let id_from_ip = ret.ip_to_id; let destinations = get_babel_info(routes, our_id, id_from_ip); - let counters = match KI.read_wg_counters(LEGACY_INTERFACE) { + let counters = match read_wg_counters(LEGACY_INTERFACE) { Ok(res) => res, Err(e) => { warn!( @@ -191,7 +192,7 @@ pub fn watch_exit_traffic( } }; - let new_counters = match KI.read_wg_counters(EXIT_INTERFACE) { + let new_counters = match read_wg_counters(EXIT_INTERFACE) { Ok(res) => res, Err(e) => { warn!( diff --git a/rita_extender/src/lib.rs b/rita_extender/src/lib.rs index 59b79d40d..73e2359a0 100644 --- a/rita_extender/src/lib.rs +++ b/rita_extender/src/lib.rs @@ -5,6 +5,8 @@ pub mod dashboard; mod error; use actix_async::System as AsyncSystem; +use althea_kernel_interface::run_command; +use althea_kernel_interface::upgrade::perform_opkg; use althea_types::OpkgCommand; use althea_types::WifiSsid; use error::RitaExtenderError; @@ -14,7 +16,6 @@ use rita_client::extender::ExtenderUpdate; use rita_common::dashboard::wifi::get_wifi_config_internal; use rita_common::dashboard::wifi::set_ssid; use rita_common::dashboard::wifi::WifiInterface; -use rita_common::KI; use std::thread; use std::time::Duration; use std::time::Instant; @@ -218,7 +219,7 @@ fn apply_opkg_update_if_needed(router_version: String, extender_version: String) feed_name: "althea_extender".to_string(), arguments: common_args, }; - let res = KI.perform_opkg(opkg_update); + let res = perform_opkg(opkg_update); match res { Ok(o) => match o.status.code() { Some(0) => info!("opkg update completed successfully! {:?}", o), @@ -240,7 +241,7 @@ fn apply_opkg_update_if_needed(router_version: String, extender_version: String) packages: vec!["rita_extender".to_string()], arguments: force_maintainer, }; - let res = KI.perform_opkg(opkg_install); + let res = perform_opkg(opkg_install); match res { Ok(o) => match o.status.code() { Some(0) => info!("opkg update completed successfully! {:?}", o), @@ -258,7 +259,7 @@ fn apply_opkg_update_if_needed(router_version: String, extender_version: String) } // Restart after opkg is complete - if let Err(e) = KI.run_command("/etc/init.d/rita_extender", &["restart"]) { + if let Err(e) = run_command("/etc/init.d/rita_extender", &["restart"]) { error!("Unable to restart rita extender after opkg update: {}", e) } } diff --git a/settings/src/lib.rs b/settings/src/lib.rs index ea3496820..34077df74 100644 --- a/settings/src/lib.rs +++ b/settings/src/lib.rs @@ -17,7 +17,7 @@ extern crate serde_derive; extern crate log; extern crate arrayvec; -use althea_kernel_interface::KI; +use althea_kernel_interface::netns::check_integration_test_netns; use althea_types::{BillingDetails, ContactType, Identity, InstallationDetails, SystemChain}; use clarity::Address; use logging::LoggingSettings; @@ -111,7 +111,7 @@ pub trait WrappedSettingsAdaptor { // Doing so will disable local reads/writes and instead call the adaptor's relevant fns // Can only be called once if no other settings exist in the SETTINGS global pub fn set_adaptor(adaptor: T) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); let mut settings_ref = SETTINGS.write().unwrap(); match settings_ref.contains_key(&netns) { // make sure this only gets called once on start @@ -156,7 +156,7 @@ impl RitaSettings { /// write the current SETTINGS from memory to file pub fn write_config() -> Result<(), SettingsError> { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); match SETTINGS.read().unwrap().get(&netns) { Some(Settings::Adaptor(adapt)) => adapt.adaptor.write_config(), Some(Settings::Client(settings)) => { @@ -191,7 +191,7 @@ pub fn save_settings_on_shutdown() { /// get a JSON value of all settings pub fn get_config_json() -> Result { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); match SETTINGS.read().unwrap().get(&netns) { Some(Settings::Adaptor(adapt)) => adapt.adaptor.get_config_json(), Some(Settings::Client(settings)) => settings.get_all(), @@ -202,7 +202,7 @@ pub fn get_config_json() -> Result { /// merge a json of a subset of settings into global settings pub fn merge_config_json(changed_settings: serde_json::Value) -> Result<(), SettingsError> { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); let mut settings_ref = SETTINGS.write().unwrap(); let settings_ref = settings_ref.get_mut(&netns); match settings_ref { @@ -217,7 +217,7 @@ pub fn merge_config_json(changed_settings: serde_json::Value) -> Result<(), Sett /// Does not currently save the identity paramater, as we don't /// need to modify that in a generic context. pub fn set_rita_common(input: RitaSettings) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); let mut settings_ref = SETTINGS.write().unwrap(); match settings_ref.get_mut(&netns) { Some(Settings::Adaptor(adapt)) => { @@ -249,7 +249,7 @@ pub fn set_rita_common(input: RitaSettings) { /// get the current settings and extract generic RitaSettings from it pub fn get_rita_common() -> RitaSettings { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); match SETTINGS.read().unwrap().get(&netns) { Some(Settings::Adaptor(adapt)) => { let settings = adapt.adaptor.get_client().unwrap(); @@ -284,19 +284,19 @@ pub fn get_git_hash() -> String { } pub fn set_flag_config(flag_config: PathBuf) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); FLAG_CONFIG.write().unwrap().insert(netns, flag_config); } pub fn get_flag_config() -> PathBuf { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); FLAG_CONFIG.read().unwrap().get(&netns).unwrap().clone() } /// set client settings into local or adaptor memory /// panics if called on exit settings pub fn set_rita_client(client_setting: RitaClientSettings) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); let mut settings_ref = SETTINGS.write().unwrap(); match settings_ref.get(&netns) { // if there's an adaptor already saved, then use it to set there @@ -317,7 +317,7 @@ pub fn set_rita_client(client_setting: RitaClientSettings) { /// get client settings from local or adaptor memory /// panics if called on exit settings pub fn get_rita_client() -> RitaClientSettings { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); match SETTINGS.read().unwrap().get(&netns) { Some(Settings::Adaptor(adapt)) => adapt.adaptor.get_client().unwrap(), Some(Settings::Client(settings)) => settings.clone(), @@ -357,7 +357,7 @@ pub fn get_system_chain() -> SystemChain { /// Set exit settings into memory pub fn set_rita_exit(exit_setting: RitaExitSettingsStruct) { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); SETTINGS .write() .unwrap() @@ -366,7 +366,7 @@ pub fn set_rita_exit(exit_setting: RitaExitSettingsStruct) { /// Retrieve exit settings from memory pub fn get_rita_exit() -> RitaExitSettingsStruct { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); let temp = SETTINGS.read().unwrap(); let temp = temp.get(&netns); if let Some(Settings::Exit(val)) = temp { @@ -378,7 +378,7 @@ pub fn get_rita_exit() -> RitaExitSettingsStruct { /// This code checks to see if the current device/setting is client or not pub fn check_if_client() -> bool { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); match SETTINGS.read().unwrap().get(&netns) { Some(Settings::Adaptor(_)) => false, Some(Settings::Client(_)) => true, @@ -389,7 +389,7 @@ pub fn check_if_client() -> bool { /// This code checks to see if the current device/setting is an exit or not pub fn check_if_exit() -> bool { - let netns = KI.check_integration_test_netns(); + let netns = check_integration_test_netns(); match SETTINGS.read().unwrap().get(&netns) { Some(Settings::Adaptor(_)) => false, Some(Settings::Client(_)) => false,