feat(mac-vendor): 本地 OUI 数据库替代在线查询,修复厂商识别失败
问题原因: - mac_vendor_lookup 库依赖从 IEEE 官网在线下载 OUI 数据库 - 其他机器无网络或网络不通时,内置精简版数据库不全 - 导致 SNMP 扫描到 MAC 地址后无法识别厂商 解决方案: 1. 从 IEEE OUI 官方数据库下载完整数据 (39782 条记录) 保存为 backend/app/data/oui.txt 纳入版本管理 2. 修改 EnhancedScanService.get_mac_vendor(),查询顺序: 1️⃣ 本地 OUI 数据库(免网络、高性能) 2️⃣ mac_vendor_lookup 在线库(回退) 3️⃣ 硬编码常用厂商映射(最终回退) 3. start.sh 启动时检测 OUI 文件是否存在,不存在自动下载 4. 支持 AA:BB:CC、AABBCC、aa-bb-cc、001c.14aa.bbcc 多种 MAC 格式
This commit is contained in:
@@ -7,6 +7,7 @@ import socket
|
||||
import ipaddress
|
||||
import re
|
||||
import logging
|
||||
import os
|
||||
|
||||
try:
|
||||
from mac_vendor_lookup import MacLookup, VendorNotFoundError
|
||||
@@ -20,6 +21,40 @@ from app.core.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# OUI 数据库文件路径(相对于项目根)
|
||||
_OUI_DB_FILE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'oui.txt')
|
||||
|
||||
|
||||
def _load_oui_database() -> Optional[Dict[str, str]]:
|
||||
"""从本地文件加载 OUI 数据库,返回 {prefix: vendor} 字典"""
|
||||
if not os.path.exists(_OUI_DB_FILE):
|
||||
logger.warning(f"OUI 数据库文件不存在: {_OUI_DB_FILE}")
|
||||
return None
|
||||
try:
|
||||
db = {}
|
||||
with open(_OUI_DB_FILE, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if ':' in line:
|
||||
prefix, vendor = line.split(':', 1)
|
||||
db[prefix] = vendor
|
||||
logger.info(f"已加载 OUI 数据库: {len(db)} 条记录, 文件: {_OUI_DB_FILE}")
|
||||
return db
|
||||
except Exception as e:
|
||||
logger.error(f"加载 OUI 数据库失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# 全局 OUI 数据库(延迟加载)
|
||||
_OUI_DB = None
|
||||
|
||||
|
||||
def _get_oui_db() -> Optional[Dict[str, str]]:
|
||||
global _OUI_DB
|
||||
if _OUI_DB is None:
|
||||
_OUI_DB = _load_oui_database()
|
||||
return _OUI_DB
|
||||
|
||||
|
||||
class EnhancedScanService:
|
||||
"""增强扫描服务 - 包含ARP、DNS解析、MAC厂商识别、NetBIOS主机名发现"""
|
||||
@@ -45,13 +80,27 @@ class EnhancedScanService:
|
||||
if len(mac) < 8:
|
||||
return None
|
||||
|
||||
# 使用 mac_vendor_lookup 库(IEEE OUI 官方数据库)
|
||||
# 提取 OUI(前 6 位十六进制 = AA:BB:CC)
|
||||
oui = mac[:8] # "AA:BB:CC"
|
||||
|
||||
# 1️⃣ 优先使用本地 OUI 数据库(免网络、高性能)
|
||||
db = _get_oui_db()
|
||||
if db:
|
||||
# 精确匹配 AA:BB:CC
|
||||
vendor = db.get(oui)
|
||||
if vendor:
|
||||
return vendor
|
||||
# 去掉冒号的格式匹配 AABBCC
|
||||
oui_no_colon = oui.replace(':', '')
|
||||
for prefix, v in db.items():
|
||||
if prefix == oui_no_colon:
|
||||
return v
|
||||
|
||||
# 2️⃣ 回退:mac_vendor_lookup 在线库
|
||||
if MAC_LOOKUP_AVAILABLE:
|
||||
lookup = EnhancedScanService._get_mac_lookup()
|
||||
if lookup is not None:
|
||||
try:
|
||||
# 提取 OUI(前 3 字节)
|
||||
oui = mac[:8] # "AA:BB:CC"
|
||||
vendor = lookup.lookup(oui)
|
||||
if vendor:
|
||||
return vendor
|
||||
@@ -60,7 +109,7 @@ class EnhancedScanService:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 回退:硬编码常用厂商映射
|
||||
# 3️⃣ 回退:硬编码常用厂商映射
|
||||
return EnhancedScanService._legacy_oui_lookup(mac)
|
||||
|
||||
# 硬编码回退映射(mac_vendor_lookup 不可用时使用)
|
||||
|
||||
Reference in New Issue
Block a user