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:
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import socket
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from mac_vendor_lookup import MacLookup, VendorNotFoundError
|
from mac_vendor_lookup import MacLookup, VendorNotFoundError
|
||||||
@@ -20,6 +21,40 @@ from app.core.config import settings
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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:
|
class EnhancedScanService:
|
||||||
"""增强扫描服务 - 包含ARP、DNS解析、MAC厂商识别、NetBIOS主机名发现"""
|
"""增强扫描服务 - 包含ARP、DNS解析、MAC厂商识别、NetBIOS主机名发现"""
|
||||||
@@ -45,13 +80,27 @@ class EnhancedScanService:
|
|||||||
if len(mac) < 8:
|
if len(mac) < 8:
|
||||||
return None
|
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:
|
if MAC_LOOKUP_AVAILABLE:
|
||||||
lookup = EnhancedScanService._get_mac_lookup()
|
lookup = EnhancedScanService._get_mac_lookup()
|
||||||
if lookup is not None:
|
if lookup is not None:
|
||||||
try:
|
try:
|
||||||
# 提取 OUI(前 3 字节)
|
|
||||||
oui = mac[:8] # "AA:BB:CC"
|
|
||||||
vendor = lookup.lookup(oui)
|
vendor = lookup.lookup(oui)
|
||||||
if vendor:
|
if vendor:
|
||||||
return vendor
|
return vendor
|
||||||
@@ -60,7 +109,7 @@ class EnhancedScanService:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 回退:硬编码常用厂商映射
|
# 3️⃣ 回退:硬编码常用厂商映射
|
||||||
return EnhancedScanService._legacy_oui_lookup(mac)
|
return EnhancedScanService._legacy_oui_lookup(mac)
|
||||||
|
|
||||||
# 硬编码回退映射(mac_vendor_lookup 不可用时使用)
|
# 硬编码回退映射(mac_vendor_lookup 不可用时使用)
|
||||||
|
|||||||
@@ -144,6 +144,26 @@ start_frontend() {
|
|||||||
npm install
|
npm install
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 检查 OUI 数据库文件
|
||||||
|
if [ ! -f "$BACKEND_DIR/app/data/oui.txt" ]; then
|
||||||
|
echo -e "${YELLOW}OUI 数据库不存在,尝试从 IEEE 下载...${NC}"
|
||||||
|
cd $BACKEND_DIR
|
||||||
|
source venv/bin/activate
|
||||||
|
python3 -c "
|
||||||
|
from mac_vendor_lookup import MacLookup
|
||||||
|
import os
|
||||||
|
os.makedirs('app/data', exist_ok=True)
|
||||||
|
lookup = MacLookup()
|
||||||
|
lookup.update_vendors()
|
||||||
|
# 复制到项目目录
|
||||||
|
import shutil
|
||||||
|
src = os.path.expanduser('~/.cache/mac-vendors.txt')
|
||||||
|
if os.path.exists(src):
|
||||||
|
shutil.copy(src, 'app/data/oui.txt')
|
||||||
|
print('OUI 数据库下载完成')
|
||||||
|
" 2>/dev/null || echo -e "${YELLOW}OUI 数据库下载失败(网络问题),使用内置硬编码回退${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
# 检查端口并清理
|
# 检查端口并清理
|
||||||
kill_port $FRONTEND_PORT
|
kill_port $FRONTEND_PORT
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user