Deduce vendor from mDNS services for privacy MACs

Modern devices increasingly use randomized ("Private WiFi Address") MACs,
which are locally administered and have no real OUI, so the MAC-prefix
vendor lookup returns nothing. Such devices still advertise distinctive
mDNS service types, which we now use to deduce their vendor.

- Extend the mDNS parser to also capture PTR service types alongside
  A-record hostnames.
- Add a service-type -> vendor mapping (data/mdns-service-vendor.json) and
  a service_vendor_finder, using canonical vendor names so device-type
  resolution still chains.
- Add utils::network::is_locally_administered to detect randomized MACs.
- In the mDNS scanner, fall back to service-based deduction only when the
  OUI lookup is empty and the MAC is locally administered.
- Group the three finders under a new data module.
- Preserve a known vendor (and its derived device_type) when a later
  sighting cannot deduce one, and suppress the spurious "vendor changed"
  notification in that case.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-05-29 13:56:39 -04:00
co-authored by Claude Opus 4.7
parent 7fb457ca4a
commit 98a23aa7fd
12 changed files with 308 additions and 32 deletions
+53
View File
@@ -0,0 +1,53 @@
use lazy_static::lazy_static;
use log::{debug, error, info};
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct MacRecord {
pub mac_prefix: String,
pub vendor_name: String,
// private: bool,
// block_type: String,
// last_update: String,
}
// Initialize mac vendors database as a static lazy loaded unit
lazy_static! {
static ref MAC_VENDORS_DATABASE: HashMap<String, String> = {
info!("Loading MAC vendor database into memory");
let mut database = HashMap::new();
let data = include_str!("../../data/mac-vendors-export.json");
let json: Vec<MacRecord> = match serde_json::from_str(data) {
Ok(value) => value,
Err(error) => {
error!(
"Error parsing mac vendors database (data/mac-vendors-export.json): {error}"
);
panic!(
"Error parsing mac vendors database (data/mac-vendors-export.json): {error}"
);
}
};
debug!("Found {} records in the database", json.len());
for el in json {
database.insert(el.mac_prefix.to_uppercase(), el.vendor_name);
}
info!("MAC vendor database loaded");
database
};
}
// Find a vendor based on the MAC prefix
pub fn find(mac_prefix: String) -> String {
MAC_VENDORS_DATABASE
.get(&mac_prefix.to_uppercase())
.unwrap_or(&"".to_string())
.to_string()
}