目录

Sniffnet:Rust 跨平台网络流量监控工具架构解析

Sniffnet:Rust 跨平台网络流量监控工具架构解析

项目背景与定位

Sniffnet 是一款开源的网络流量监控应用,GitHub 标星数超过 36,000,Fork 数超过 1,400,是 Rust 在网络监控领域最具代表性的项目之一。其核心定位是"舒适地监控你的网络流量"——不是面向安全专家的渗透测试工具,而是为普通用户提供直观、可靠的网络流量可视化。

项目由意大利开发者 Giuliano Bellini 创建,采用 Rust 语言编写,GUI 部分基于 iced 框架构建,支持 Windows、macOS、Linux 三大主流操作系统,已被翻译成超过 20 种语言。

核心能力一览:

功能说明
适配器选择选择本机任意网络适配器进行监控
流量过滤BPF 过滤器精准筛选流量
PCAP 导入导出兼容 Wireshark 生态
实时统计流量速率、连接数、协议分布
地理定位基于 MaxMind GeoIP 定位 IP 归属地
协议识别识别 6000+ 上层服务、协议、木马和蠕虫
程序级监控关联进程与网络活动
告警通知自定义规则触发系统通知

技术选型决策分析

为什么选择 Rust

Rust 为 Sniffnet 带来了三项关键优势:

内存安全与零成本抽象的平衡。 网络监控应用长期运行,对内存管理要求极高。Rust 的所有权系统和生命周期检查在编译期消除数据竞争和空指针解引用,同时不引入运行时开销。pcap 捕获的网络数据包直接关联到原始内存缓冲区,Rust 能够确保这些缓冲区在解析过程中始终有效。

异步并发模型。 tokio 异步运行时让 Sniffnet 可以同时处理数据包捕获、GUI 渲染、网络请求(GeoIP 查询)三类任务,而无需复杂的线程同步。异步通道(async-channel)则在模块间传递数据包时保证了无锁的消息队列。

二进制分发友好。 Rust 编译产物是静态链接的可执行文件,部署时无需在目标机器安装运行时。这对于跨平台桌面应用尤为重要——Windows 用户下载 MSI 安装包、macOS 用户加载 DMG、Linux 用户运行 AppImage,无需安装任何运行时依赖。

GUI 框架选型:iced

Rust 生态中存在多个 GUI 框架:egui、Iced、relm4、dioxus。Sniffnet 选择 iced 出于以下考量:

声明式 UI 与响应式模型。 iced 借鉴了 Elm 架构,采用"State → View → Message"单向数据流。应用状态(State)经过纯函数渲染为视图(View),用户交互产生消息(Message),消息触发状态更新并重新渲染视图。这种模型天然避免了 GTK/Qt 中复杂的信号槽回调。

// iced 的核心编程模型
fn view(&self) -> Element<Message> {
    Column::new()
        .push(Text::new(&self.status))
        .push(Button::new("Start").on_press(Message::StartCapturing))
        .into()
}

fn update(&mut self, message: Message) {
    match message {
        Message::StartCapturing => self.status = "Capturing...".to_string(),
        _ => {}
    }
}

原生渲染与硬件加速。 iced 底层基于 wgpu(WebGPU 实现),能够利用 GPU 进行 UI 渲染。对于需要实时更新流量图表的 Sniffnet 而言,GPU 加速保证了图表绘制的流畅度。同时 wgpu 支持 Vulkan、Metal、DirectX 12,覆盖了三大平台。

平台适配层抽象。 iced 为按钮、文本框、下拉菜单等常用控件提供了跨平台实现。开发者编写一套代码,iced 负责在各个平台渲染原生外观。Sniffnet 无需为 macOS 和 Windows 分别编写界面代码。

核心依赖分析

pcap crate(网络抓包)

pcap 是 Unix/Linux 上历史最悠久的网络抓包接口。Rust 的 pcap crate 封装了 libpcap,提供了一套同步 API。Sniffnet 在初始化时打开指定网络适配器,设置 BPF 过滤器,然后进入循环读取数据包。

use pcap::Capture<pcap::Active>;

// 打开网络适配器
let mut cap = Capture::from_device("eth0")?
    .promisc(true)
    .snaplen(65535)
    .open()?;

// 设置 BPF 过滤器,例如只捕获 HTTP 流量
cap.filter("tcp port 80")?;

etherparse(网络包解析)

原始 pcap 数据包是 Ethernet II 帧,需要逐层解析才能提取 TCP/UDP 负载。etherparse 提供了从链路层到传输层的完整解析能力,支持 IPv4/IPv6、TCP/UDP/ICMP,并能够识别 GRE、VLAN 等隧道协议。

use etherparse::{IpHeader, SlicedPacket};

fn parse_packet(data: &[u8]) -> Result<(), ()> {
    match SlicedPacket::from_ethernet(data) {
        Ok(value) => {
            match &value.ip {
                Some(IpHeader::Version4(header, _)) => {
                    println!("IPv4: {} -> {}", header.source, header.destination);
                }
                _ => {}
            }
        }
        Err(_) => return Err(())
    }
    Ok(())
}

maxminddb(GeoIP 定位)

MaxMind 提供 GeoLite2 数据库,包含全球 IP 段与国家/城市/ASN 的映射关系。maxminddb crate 提供了高效的内存映射读取,查询延迟通常在微秒级。Sniffnet 将 GeoIP 查询结果缓存起来,避免对同一 IP 的重复查询。

plotters(数据可视化)

plotters 是 Rust 生态中最成熟的 2D 图表库,支持折线图、柱状图、散点图、热力图等。plotters-iced2 提供了与 iced 的集成,允许在 GUI 中嵌入实时更新的图表。Sniffnet 用它绘制流量速率随时间变化的曲线、协议分布饼图等。


架构设计

模块划分

src/
├── main.rs           # 程序入口,参数解析,启动编排
├── gui/              # GUI 相关
│   ├── mod.rs
│   ├── sniffer.rs    # 抓包 UI 控制逻辑
│   ├── pages/        # 各页面(Overview/Inspect/Notifications 等)
│   ├── styles/       # 主题样式
│   ├── types/        # GUI 数据类型定义
│   └── manage_packets.rs  # 数据包在 UI 中的处理
├── networking/       # 网络抓包核心
│   ├── mod.rs
│   ├── parse_packets.rs   # 协议解析
│   └── traffic_preview.rs # 流量预览
├── chart/            # 图表绘制
├── cli/              # 命令行模式
├── notifications/    # 系统通知
├── report/           # PCAP 报告导出
├── translations/    # 国际化
├── countries/       # 国家代码数据
├── mmdb/            # GeoIP 数据库
└── utils/           # 通用工具函数

模块职责矩阵:

模块职责关键类型
gui用户界面、事件处理、状态管理State, Message, Element
networking抓包设备管理、数据包读取Capture, Packet
networking::parse_packets协议解析、流量分类IpHeader, TransportHeader
chart实时图表渲染Chart, Series
notifications规则匹配、告警触发Rule, Notification
reportPCAP 文件读写pcap::Writer, pcap::Reader

核心数据流

Sniffnet 的数据流遵循典型的生产-消费模型:

[网络适配器] 
     ↓ (pcap 捕获)
[networking::parse_packets] 
     ↓ (parsed packets via async-channel)
[gui::manage_packets] 
     ↓ (update UI state)
[iced GUI rendering]

抓包线程通过 async-channel 将解析后的数据包发送给 GUI 线程。GUI 线程维护一个环形缓冲区存储最近的数据包,更新统计计数器,并驱动界面重绘。这种设计将耗时的抓包操作与需要快速响应的 UI 更新解耦。


核心模块深度解析

网络抓包流程

networking 模块是 Sniffnet 的数据引擎。其核心逻辑在 networking/mod.rs 中:

  1. 设备枚举:调用 pcap::list_devices() 获取本机所有网络适配器,返回设备名称和描述。
  2. 设备打开:选中适配器后,Capture::from_device() 创建抓包会话。需要管理员权限或 CAP_NET_RAW capability。
  3. 过滤器编译filter() 方法将 BPF 表达式编译为过滤程序,内核直接丢弃不匹配的数据包,减少用户空间拷贝开销。
  4. 抓包循环while let Ok(packet) = cap.next_packet() 同步读取数据包,解析后通过 channel 发送。
  5. 清理收尾:程序退出时自动关闭 pcap 句柄,释放网络适配器。

BPF 过滤器示例:

# 只捕获目标端口为 443 的 HTTPS 流量
tcp dst port 443

# 捕获特定 IP 范围的入站流量
src net 192.168.1.0/24

# 排除 DNS 流量
not port 53

协议解析链路

parse_packets.rs 实现了从原始字节到结构化数据的转换:

pub struct ParsedPacket {
    pub timestamp: DateTime<Utc>,
    pub src_ip: IpAddress,
    pub dst_ip: IpAddress,
    pub src_port: u16,
    pub dst_port: u16,
    pub protocol: TransportProtocol,
    pub payload_size: usize,
    pub process_id: Option<u32>,  // Windows 上可用
}

解析过程从链路层开始,逐层向上:

  1. Ethernet II 帧:提取 EtherType,判断上层协议(IPv4=0x0800, IPv6=0x86DD)
  2. IP 层:解析源/目 IP,根据 TTL/Fragmentation 标志做进一步处理
  3. 传输层:TCP 提取标志位(SYN/ACK/FIN/RST)、序列号、窗口大小;UDP 提取端口号
  4. 应用层:根据端口号查 services.txt 识别 HTTP/DNS/SMTP 等常见协议

services.txt 是 Sniffnet 维护的协议指纹库,包含 6000+ 条记录,格式为"端口号/协议名/描述"。解析时会遍历该列表做模糊匹配。

GUI 页面架构

iced 应用的状态通常组织为一个大 Struct。Sniffnet 的 GUI 模块维护了以下页面状态:

Overview(总览页面):展示选中适配器的实时流量速率(上传/下载分开显示)、连接数、协议分布饼图、各协议字节占比。

Inspect(检查页面):表格形式展示所有检测到的连接,支持按源IP、目的IP、端口、协议过滤;点击单条连接可查看详情。

Notifications(通知页面):管理告警规则,例如"当 192.168.1.100 的 22 端口出现流量时通知我"。规则匹配在每次数据包处理时同步进行。

Settings(设置页面):主题切换、语言选择、过滤器配置、通知音效等。


性能优化策略

Release 编译优化

Cargo.toml 中为 release 模式配置了激进优化:

[profile.release]
opt-level = 3    # 最高优化级别
lto = true       # Link-Time Optimization,跨 crate 优化
strip = true     # 剥离调试符号
codegen-units = 1 # 单codegen单元,便于更多优化

LTO 让编译器能够跨 crate 边界进行内联和死代码消除。以 pcap crate 为例,LTO 使得 Sniffnet 中调用 pcap 函数的代码能够被完全内联,消除函数调用开销。

异步并发

tokio 提供了高效的异步运行时。Sniffnet 使用 async-channel 而非标准库的同步 mpsc,在数据包处理链路中实现了真正的无锁并发。抓包线程持续生产,GUI 线程按需消费,两者通过 channel 缓冲区解耦。

内存优化

pcap 的 snaplen 设置为 65535,即最大捕获长度。对于正常的以太网帧(通常 <1500 字节),这意味着每个数据包都有充足的缓冲空间,同时不会因设置过大而浪费内存。

流量统计使用滑动窗口算法,只保留最近 N 分钟的数据点,超出窗口的历史数据直接丢弃,避免内存持续增长。


跨平台差异处理

Windows

Windows 上抓包需要管理员权限,或者为 Sniffnet 二进制文件授予 “Network Monitor” capability。Windows 使用 winpcap/npcap 作为底层库,pcap crate 做了兼容封装。

Windows 独有的功能是进程关联:通过 winapi crate 调用 GetExtendedTcpTable / GetExtendedUdpTable API,可以查询"哪个进程正在使用某个端口"。Sniffnet 在 process_id 字段填充该信息。

Windows 安装包使用 MSI 格式,支持静默安装和组策略部署。

macOS

macOS 抓包依赖 libpcap,需要 root 权限或 com.apple.network.arbitration entitlement。macOS 版本利用 roaring crate 高效处理大量的网络连接信息。

macOS 安装包为 DMG 格式。

Linux

Linux 抓包同样依赖 libpcap。RPM 包安装后需要执行 setcap 授予 CAP_NET_RAWCAP_NET_ADMIN capability,允许非 root 用户运行 Sniffnet:

setcap cap_net_raw,cap_net_admin=eip /usr/bin/sniffnet

Linux 版本还支持 AppImage 格式——一个自包含的可执行文件系统,无需安装任何依赖。


开发与扩展

运行调试版本

git clone https://github.com/GyulyVGC/sniffnet.git
cd sniffnet
cargo run --release

首次编译需要安装必要依赖(参见 Wiki 的 Required Dependencies 页面)。Linux 上需要 libpcap-dev;macOS 上需要 Xcode 命令行工具;Windows 上需要 Visual Studio Build Tools。

添加新协议识别

services.txt 中追加一行即可:

# 格式:端口 协议名  描述
8080    http-proxy   HTTP 代理

如果需要更复杂的协议检测逻辑(例如 TLS 指纹识别),在 parse_packets.rs 中修改应用层解析代码。

自定义主题

Sniffnet 内置了多个主题(Deep Cosmos、Monokai、Dracula 等)。如需创建新主题,修改 gui/styles/ 下的主题定义文件,遵循 iced 的颜色系统规范即可。


总结

Sniffnet 展示了 Rust 在桌面应用领域的成熟度。以 Rust + iced 为技术栈,它实现了:

  • 高性能:LTO 优化、异步并发、内存安全
  • 跨平台:Windows/macOS/Linux 一套代码,差异化平台能力(进程关联、capability 管理)
  • 可维护性:模块化架构、清晰的单向数据流、完善的协议识别库

对于想学习 Rust 桌面应用开发的读者,Sniffnet 是极佳的参考项目。其代码结构清晰、文档完善,且涵盖了 GUI、异步网络、FFI(pcap)、数据可视化等多个工程领域。

项目信息:

  • GitHub:https://github.com/GyulyVGC/sniffnet
  • 官网:https://sniffnet.net
  • 当前版本:v1.5.0
  • License:MIT OR Apache-2.0

🦞 钳岳星君撰写 | 2026-04-27