Esqueleto inicial: sabia-core + sabia-cli + UI local, OpenWolf
- sabia-core: descoberta de interfaces, controle de modo monitor/canal via iw (ponte até ter nl80211 nativo), captura crua 802.11 via AF_PACKET (sem libpcap), parsing de radiotap e quadros de gerenciamento (beacon/probe/EAPOL), agregação em tabela de APs/ clientes, escrita de .pcap, trilha de auditoria encadeada por hash, auto-atualização via GitHub releases (curl, sem dependências de rede extras) - sabia-cli: `sabia iface`, `sabia scan`, `sabia update`, `sabia audit-verify`, `sabia ui` (servidor local + navegador, protegido por token, mesma filosofia do Guará) - OpenWolf inicializado para memória de projeto entre agentes
This commit is contained in:
commit
e989413018
76 files changed
+8386
No files matched your search
@@ -0,0 +1,110 @@
|
||||
//! Descoberta de interfaces de rede e identificação das que são Wi-Fi.
|
||||
//!
|
||||
//! Não depende de `iw`: lê direto de `/sys/class/net`, do mesmo jeito que o
|
||||
//! próprio `iw`/`ip` fazem por baixo.
|
||||
|
||||
use crate::error::{Result, SabiaError};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct Interface {
|
||||
pub name: String,
|
||||
pub mac: String,
|
||||
/// Caminho do phy (ex.: `phy0`), presente só em interfaces 802.11.
|
||||
pub phy: Option<String>,
|
||||
pub mode: Mode,
|
||||
pub up: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
|
||||
pub enum Mode {
|
||||
Managed,
|
||||
Monitor,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn net_root() -> &'static Path {
|
||||
Path::new("/sys/class/net")
|
||||
}
|
||||
|
||||
/// Lista todas as interfaces de rede, Wi-Fi ou não (chamador filtra por `phy`).
|
||||
pub fn list_all() -> Result<Vec<Interface>> {
|
||||
let mut out = Vec::new();
|
||||
for entry in fs::read_dir(net_root())? {
|
||||
let entry = entry?;
|
||||
let name = entry.file_name().to_string_lossy().into_owned();
|
||||
if name == "lo" {
|
||||
continue;
|
||||
}
|
||||
out.push(read_interface(&name)?);
|
||||
}
|
||||
out.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Lista apenas interfaces com um `phy80211` (ou seja, rádios Wi-Fi).
|
||||
pub fn list_wireless() -> Result<Vec<Interface>> {
|
||||
Ok(list_all()?.into_iter().filter(|i| i.phy.is_some()).collect())
|
||||
}
|
||||
|
||||
pub fn get(name: &str) -> Result<Interface> {
|
||||
let iface = read_interface(name)?;
|
||||
if iface.phy.is_none() {
|
||||
return Err(SabiaError::NotWireless(name.to_string()));
|
||||
}
|
||||
Ok(iface)
|
||||
}
|
||||
|
||||
fn read_interface(name: &str) -> Result<Interface> {
|
||||
let base: PathBuf = net_root().join(name);
|
||||
if !base.exists() {
|
||||
return Err(SabiaError::InterfaceNotFound(name.to_string()));
|
||||
}
|
||||
|
||||
let mac = fs::read_to_string(base.join("address"))
|
||||
.map(|s| s.trim().to_string())
|
||||
.unwrap_or_default();
|
||||
|
||||
let up = fs::read_to_string(base.join("operstate"))
|
||||
.map(|s| s.trim() == "up")
|
||||
.unwrap_or(false);
|
||||
|
||||
let phy80211 = base.join("phy80211");
|
||||
let phy = if phy80211.is_dir() {
|
||||
fs::read_link(&phy80211)
|
||||
.ok()
|
||||
.and_then(|p| p.file_name().map(|f| f.to_string_lossy().into_owned()))
|
||||
.or_else(|| {
|
||||
// em alguns kernels phy80211 é um diretório real, não symlink;
|
||||
// o nome do phy então vem do subdiretório "phyNN" dentro dele.
|
||||
fs::read_dir(&phy80211)
|
||||
.ok()
|
||||
.and_then(|mut rd| rd.next())
|
||||
.and_then(|e| e.ok())
|
||||
.map(|e| e.file_name().to_string_lossy().into_owned())
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mode = if phy.is_some() {
|
||||
read_wireless_mode(name)
|
||||
} else {
|
||||
Mode::Unknown
|
||||
};
|
||||
|
||||
Ok(Interface { name: name.to_string(), mac, phy, mode, up })
|
||||
}
|
||||
|
||||
/// Lê o modo (`managed`/`monitor`) via `/sys/class/net/<if>/type` + `uevent`
|
||||
/// não é suficiente (type é sempre ARPHRD_ETHER para Wi-Fi); a forma estável
|
||||
/// sem libnl é delegar a leitura real ao `iw dev <if> info` quando disponível,
|
||||
/// mantendo aqui um melhor-esforço local para não depender de root.
|
||||
fn read_wireless_mode(name: &str) -> Mode {
|
||||
match crate::netlink::iw_dev_type(name) {
|
||||
Some(Mode::Monitor) => Mode::Monitor,
|
||||
Some(Mode::Managed) => Mode::Managed,
|
||||
_ => Mode::Unknown,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user