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,95 @@
|
||||
//! Controle de modo (managed/monitor) e canal via `iw`.
|
||||
//!
|
||||
//! Isso é uma ponte deliberada: o protocolo real é nl80211 por netlink, e
|
||||
//! trocar este módulo por uma implementação nativa (sem depender do binário
|
||||
//! `iw` instalado) é o próximo passo natural. Por ora, `iw` é amplamente
|
||||
//! disponível, testado e evita reimplementar genl/nl80211 antes de validar
|
||||
//! o resto do pipeline com o adaptador real.
|
||||
|
||||
use crate::error::{Result, SabiaError};
|
||||
use crate::iface::Mode;
|
||||
use std::process::Command;
|
||||
|
||||
fn run(cmd: &str, args: &[&str]) -> Result<String> {
|
||||
let out = Command::new(cmd).args(args).output().map_err(|e| SabiaError::CommandFailed {
|
||||
cmd: format!("{cmd} {}", args.join(" ")),
|
||||
detail: e.to_string(),
|
||||
})?;
|
||||
if !out.status.success() {
|
||||
let detail = String::from_utf8_lossy(&out.stderr).trim().to_string();
|
||||
let detail = if detail.is_empty() {
|
||||
String::from_utf8_lossy(&out.stdout).trim().to_string()
|
||||
} else {
|
||||
detail
|
||||
};
|
||||
if detail.contains("Operation not permitted") || detail.contains("nl80211 not found") {
|
||||
return Err(SabiaError::PermissionDenied);
|
||||
}
|
||||
return Err(SabiaError::CommandFailed {
|
||||
cmd: format!("{cmd} {}", args.join(" ")),
|
||||
detail,
|
||||
});
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&out.stdout).into_owned())
|
||||
}
|
||||
|
||||
pub fn iw_dev_type(iface: &str) -> Option<Mode> {
|
||||
let out = run("iw", &["dev", iface, "info"]).ok()?;
|
||||
for line in out.lines() {
|
||||
let line = line.trim();
|
||||
if let Some(t) = line.strip_prefix("type ") {
|
||||
return Some(match t.trim() {
|
||||
"monitor" => Mode::Monitor,
|
||||
"managed" => Mode::Managed,
|
||||
_ => Mode::Unknown,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Lista (canal, frequência) suportados pelo phy da interface.
|
||||
pub fn supported_channels(iface: &str) -> Result<Vec<u32>> {
|
||||
let out = run("iw", &["dev", iface, "info"])?;
|
||||
let phy = out
|
||||
.lines()
|
||||
.find_map(|l| l.trim().strip_prefix("wiphy "))
|
||||
.map(|n| format!("phy{n}"))
|
||||
.ok_or_else(|| SabiaError::CommandFailed {
|
||||
cmd: "iw dev info".into(),
|
||||
detail: "wiphy não encontrado na saída".into(),
|
||||
})?;
|
||||
let info = run("iw", &["phy", &phy, "info"])?;
|
||||
let mut channels = Vec::new();
|
||||
for line in info.lines() {
|
||||
let line = line.trim();
|
||||
// formato: "* 2412 MHz [1] (20.0 dBm)"
|
||||
if let Some(rest) = line.strip_prefix("* ") {
|
||||
if let Some(start) = rest.find('[') {
|
||||
if let Some(end) = rest[start..].find(']') {
|
||||
if let Ok(ch) = rest[start + 1..start + end].parse::<u32>() {
|
||||
channels.push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
channels.sort_unstable();
|
||||
channels.dedup();
|
||||
Ok(channels)
|
||||
}
|
||||
|
||||
/// Põe a interface em modo monitor. Requer `CAP_NET_ADMIN` (normalmente root).
|
||||
pub fn set_monitor_mode(iface: &str, enable: bool) -> Result<()> {
|
||||
run("ip", &["link", "set", iface, "down"])?;
|
||||
let target = if enable { "monitor" } else { "managed" };
|
||||
run("iw", &["dev", iface, "set", "type", target])?;
|
||||
run("ip", &["link", "set", iface, "up"])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Troca o canal atual da interface (precisa já estar em modo monitor).
|
||||
pub fn set_channel(iface: &str, channel: u32) -> Result<()> {
|
||||
run("iw", &["dev", iface, "set", "channel", &channel.to_string()])?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user