Added insertion and deletion of found devices to database

This commit is contained in:
rzuasti
2026-01-21 10:30:37 -05:00
parent f9c625c7d7
commit f094ecd1ff
9 changed files with 245 additions and 17 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
[env] [env]
RUST_LOG="debug" RUST_LOG="info"
OOTT_ARP_SENDER_TIMEOUT="30" OOTT_ARP_SENDER_TIMEOUT="30"
OOTT_ARP_SCAN_DURATION="60" OOTT_ARP_SCAN_DURATION="60"
Generated
+122
View File
@@ -11,6 +11,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.21" version = "0.6.21"
@@ -61,6 +70,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.10.0" version = "2.10.0"
@@ -95,12 +110,31 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.4" version = "0.1.4"
@@ -182,6 +216,30 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "include_dir" name = "include_dir"
version = "0.7.4" version = "0.7.4"
@@ -311,6 +369,15 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.3"
@@ -327,6 +394,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
name = "oott" name = "oott"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono",
"env_logger", "env_logger",
"include_dir", "include_dir",
"log", "log",
@@ -552,6 +620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono",
"fallible-iterator", "fallible-iterator",
"fallible-streaming-iterator", "fallible-streaming-iterator",
"hashlink", "hashlink",
@@ -820,12 +889,65 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.60.2" version = "0.60.2"
+2 -1
View File
@@ -10,6 +10,7 @@ pnet = "0.35.0"
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
rusqlite = { version = "0.38.0", features = ["bundled"] } rusqlite = { version = "0.38.0", features = ["bundled", "chrono"] }
rusqlite_migration = { version = "2.4.0", features = ["from-directory"] } rusqlite_migration = { version = "2.4.0", features = ["from-directory"] }
include_dir = "0.7.4" include_dir = "0.7.4"
chrono = "0.4.43"
@@ -1,6 +1,6 @@
CREATE TABLE devices( CREATE TABLE devices(
mac_address TEXT PRIMARY KEY, mac_address TEXT PRIMARY KEY,
ip_address TEXT NOT NULL, ipv4_address TEXT NOT NULL,
vendor_name TEXT, vendor TEXT,
last_seen TEXT NOT NULL last_seen TEXT NOT NULL
); );
+2
View File
@@ -1,3 +1,5 @@
pub mod devices;
use include_dir::{Dir, include_dir}; use include_dir::{Dir, include_dir};
use log::{debug, error}; use log::{debug, error};
use rusqlite::Connection; use rusqlite::Connection;
+71
View File
@@ -0,0 +1,71 @@
use log::{debug, error};
use rusqlite::{Connection, params};
use std::sync::MutexGuard;
use crate::device_finders::Device;
// Read device from its MAC address
pub fn read(conn: MutexGuard<Connection>, mac_address: String) -> Option<Device> {
// TODO: Add last_seen
let result: Result<Device, rusqlite::Error> = conn.query_one(
"SELECT mac_address, ipv4_address, vendor, last_seen FROM devices WHERE mac_address=?1",
params![mac_address],
|row| {
Ok(Device {
mac_address: row.get(0)?,
ipv4_address: row.get(1)?,
vendor: row.get(2)?,
last_seen: row.get(3)?,
})
},
);
match result {
Ok(value) => Some(value),
Err(error) => {
match error {
rusqlite::Error::QueryReturnedNoRows => {
debug!("No device found for MAC address {mac_address}.")
}
_ => error!("Error reading device from database: {error}"),
};
None
}
}
}
pub fn insert(conn: MutexGuard<Connection>, device: Device) -> Result<(), String> {
match conn.execute(
"INSERT INTO devices (mac_address, ipv4_address, vendor, last_seen) VALUES (?1, ?2, ?3, ?4)",
params![device.mac_address, device.ipv4_address, device.vendor, device.last_seen]) {
Ok(_) => {
debug!("Device inserted into database: {}", device);
Ok(())
},
Err(error) => {
error!("Error inserting device ({device}) into database: {error}");
Err(format!("Error inserting device ({device}) into database: {error}").to_string())
},
}
}
pub fn update(conn: MutexGuard<Connection>, device: Device) -> Result<(), String> {
match conn.execute(
"UPDATE devices SET ipv4_address=?1, vendor=?2, last_seen=?3 WHERE mac_address=?4",
params![
device.ipv4_address,
device.vendor,
device.last_seen,
device.mac_address
],
) {
Ok(_) => {
debug!("Device updated in database: {}", device);
Ok(())
}
Err(error) => {
error!("Error updating device ({device}) in database: {error}");
Err(format!("Error updating device ({device}) in database: {error}").to_string())
}
}
}
+5 -8
View File
@@ -1,25 +1,22 @@
pub mod arp; pub mod arp;
use chrono::NaiveDateTime;
use std::fmt; use std::fmt;
#[derive(Clone)]
pub struct Device { pub struct Device {
pub mac_address: String, pub mac_address: String,
pub ipv4_address: String, pub ipv4_address: String,
pub vendor: String, pub vendor: String,
} pub last_seen: NaiveDateTime,
impl Device {
// pub fn get_mac_prefix(&self) -> String {
// self.mac_address.get(0..8).unwrap_or("").to_string()
// }
} }
impl fmt::Display for Device { impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"mac={}, ip={}, vendor={}", "mac={}, ip={}, vendor={}, last_seen={}",
self.mac_address, self.ipv4_address, self.vendor self.mac_address, self.ipv4_address, self.vendor, self.last_seen
) )
} }
} }
@@ -1,5 +1,6 @@
use crate::device_finders::Device; use crate::device_finders::Device;
use crate::mac_vendor_finder; use crate::mac_vendor_finder;
use chrono::Local;
use log::{debug, info}; use log::{debug, info};
use pnet::datalink::{DataLinkReceiver, DataLinkSender, NetworkInterface}; use pnet::datalink::{DataLinkReceiver, DataLinkSender, NetworkInterface};
use pnet::ipnetwork::Ipv4Network; use pnet::ipnetwork::Ipv4Network;
@@ -99,6 +100,7 @@ pub async fn listen_for_packets(
mac_address: packet_mac_address, mac_address: packet_mac_address,
ipv4_address: packet_ip_address, ipv4_address: packet_ip_address,
vendor: packet_vendor, vendor: packet_vendor,
last_seen: Local::now().naive_local(),
}); });
} }
} }
+38 -5
View File
@@ -1,4 +1,5 @@
use log::{debug, info}; use log::{debug, info};
use std::sync::{Arc, Mutex};
mod config; mod config;
mod db; mod db;
@@ -6,22 +7,54 @@ mod device_finders;
mod mac_vendor_finder; mod mac_vendor_finder;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), String> {
env_logger::init(); env_logger::init();
info!("Starting up oott"); info!("Starting up oott");
let db_conn = db::init_db(); // Get database connection - thread protected
let db_conn = Arc::new(Mutex::new(db::init_db().unwrap()));
// Find online devices via ARP
let devices = device_finders::arp::find("eno1").await.unwrap(); let devices = device_finders::arp::find("eno1").await.unwrap();
info!("Done with ARP probes"); info!("Done with ARP probes");
info!("Found {} online devices", devices.iter().count()); info!("Found {} online devices", devices.iter().count());
// mac_vendor_finder.populate_vendors(&devices); // Process found devices
for device in devices.iter() { for device in devices.iter() {
debug!("Device found {}", device); debug!("Online device found {}", device);
// Using just one connection for now, need to update if moved DB portion to multi-thread
// need to change to a clone if we need multiple threads
let db_conn_clone = Arc::clone(&db_conn);
// Read device from database
let recorded_device_result =
db::devices::read(db_conn_clone.lock().unwrap(), device.mac_address.clone());
match recorded_device_result {
Some(recorded_device) => {
// If it exists update its last seen date
info!(
"Device found in database {}. Updating to {}.",
recorded_device, device
);
// TODO: If IPv4 or vendor changed do something
db::devices::update(db_conn_clone.lock().unwrap(), device.clone());
}
None => {
// If it doesn't exist insert it
info!(
"Device with MAC address {} not found in database. Inserting it.",
device.mac_address
);
db::devices::insert(db_conn_clone.lock().unwrap(), device.clone());
}
};
} }
info!("Exiting"); info!("Exiting");
Ok(())
} }