跳到正文

目录

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

快速信息卡

属性
GitHub Stars37,000+
GitHub Forks1,400+
主要语言Rust(edition 2024)
开源协议MIT OR Apache-2.0
GUI 框架iced 0.14(wgpu 加速)
当前版本v1.5.1
项目定位跨平台网络流量监控工具

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

Sniffnet 真正解决的不是"怎么抓到数据包"——抓包这件事 libpcap 早就做完了。它解决的是两个更难的问题:把一瞬间的原始字节流,整理成普通人能一眼看懂的连接视图;以及让这一过程在 Windows、macOS、Linux 三套权限模型下都跑得通。理解它的架构,关键在看清它如何在"抓包"和"展示"之间切出一层清晰的分隔,并把跨平台的差异化悄悄塞进这一层的边界里。

学习目标

读完本文后,你应该能够:

  • 理解 Sniffnet 的技术选型理由(为什么选择 Rust + iced)
  • 区分 Sniffnet 的模块职责(networking、gui、chart、notifications)
  • 成功从源码构建 Sniffnet,并理解 pcap 抓包流程
  • 针对你的场景判断 Sniffnet 是否合适(替代 Wireshark?补充监控?)
  • 理解跨平台差异处理(Windows 权限、macOS 授权、Linux capabilities、程序识别来源差异)

目录


项目背景与定位

Sniffnet 是意大利开发者 Giuliano Bellini 用 Rust 写的开源网络流量监控应用,GitHub 标星超过 37,000,Fork 超过 1,400,支持 Windows、macOS、Linux 三大平台,界面翻译超过 20 种语言。

它想服务的既不是资深安全研究员的取证需求,也不是运维手里的全流量分析平台,而是"我想知道我电脑现在到底在和谁说话"的普通用户。这个定位决定了它靠直觉完成的事,多数监控工具要靠命令行参数完成。

能力一览:

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

整体架构:两块主线和一条边界

读代码前,先抓住 Sniffnet 内部其实是两条在时间尺度上完全不同的主线:

  • 抓包链路(高频、铁定顺序):网卡 → pcap → 协议解析 → 结构化数据包。这条线只做一件事,不关心界面。
  • 展示链路(低频、响应交互):连接状态聚合 → 图表 → 页面渲染。这条线关心的是"当前有哪些连接、速率多少",而不是单个数据包。

两条线之间用一条异步通道隔开,通道传送的是已经解析好的 ParsedPacket。这条边界的意义在于:抓包线程永远不被界面卡住拖慢,而界面线程也永远不需要面对原始字节。

[网络适配器]
     ↓ pcap 捕获(内核 BPF 先过滤一层)
[networking::parse_packets]     抓包链路
     ↓ 解析为 ParsedPacket
[async-channel]                 ←── 边界(解耦点)
     ↓
[gui::manage_packets]           展示链路
     ↓ 更新统计与环形缓冲
[iced GUI rendering]

模块职责矩阵:

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

技术选型决策分析

为什么选择 Rust

Rust 给 Sniffnet 带来三个实际好处。

内存安全与零成本抽象的平衡。 网络监控应用长时间运行,抓到的每个数据包都绑定到原始缓冲区的生命周期。Rust 的所有权和借用检查在编译期保证这些缓冲区在解析期间始终有效,杜绝悬垂引用和数据竞争,又不引入运行时开销。对一个从网卡持续灌数据的程序来说,这直接决定了它能跑多久不出事。

异步并发模型。 tokio 让抓包、渲染、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” 模型:应用状态是一个纯数据结构,通过纯函数渲染成界面;用户交互产生消息,消息驱动状态更新,状态再触发重绘。这个模型天然避开了 GTK/Qt 那套信号槽回调,也让状态可以整体被序列化、测试和复现。

// iced 的编程模型:view 是纯函数,update 应答消息
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,能吃到 GPU 渲染红利。对流量图表这种高频重绘的界面,CPU 软件渲染很容易成为瓶颈,wgpu 通过 Vulkan、Metal、DirectX 12 把三套平台统一到同一份代码。

跨平台控件抽象。 iced 为按钮、输入框、下拉菜单提供了统一的实现,写一套界面代码,三端渲染成各自的原生外观,Sniffnet 不用为每个平台单独写 UI。

关键依赖分析

pcap crate(网络抓包)

Rust 的 pcap crate 封装了 libpcap。Sniffnet 在初始化时打开指定适配器、设置 BPF 过滤器,然后进入循环读包。pcap crate 用的是同步 API,Sniffnet 把它放进一个专门的异步任务里跑,避免阻塞其他部分。

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 把查询结果缓存起来,同一 IP 只查一次,避免反复命中磁盘映射。

plotters(数据可视化)

plotters 是 Rust 生态里最成熟的 2D 图表库,折线、柱状、散点、热力图都有。plotters-iced2 把它接到 iced 里,让 Sniffnet 能在界面内嵌动态刷新的速率曲线和协议分布图。


主要模块深度解析

网络抓包流程

networking 模块是数据引擎,核心逻辑在 networking/mod.rs。它有五步:

  1. 设备枚举pcap::Device::list() 拉出本机所有适配器,带名称和描述。
  2. 设备打开:选定适配器后,Capture::from_device() 建会话。Linux 需要 CAP_NET_RAW 权限。
  3. 过滤器编译filter() 把 BPF 表达式编成内核过滤程序,不匹配的包在内核就被丢掉,减少进用户空间的拷贝。
  4. 抓包循环while let Ok(packet) = cap.next_packet() 同步读包,解析后经通道发出去。
  5. 收尾:程序退出自动关 pcap 句柄,释放适配器。

常用 BPF 过滤器:

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

# 捕获特定网段的入站流量
src net 192.168.1.0/24

# 排除 DNS 流量
not port 53

协议识别与端口映射

parse_packets.rsSlicedPacket 转成应用需要的结构化数据:

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>,  // v1.5 起三端可用
}

应用层识别依赖项目根目录的 services.txt。这份文件有看点:它不在运行时被反复遍历build.rs 在编译期把它读进来,烘焙进一张由 phf 生成的静态完美哈希表,覆盖 6000+ 上层服务(含协议、木马、蠕虫)。也就是说,运行时根据"端口 + 传输协议"定位服务名是 O(1) 的精确查找,代价是改一次表就要重编译。这个取舍对静态文件合理,但对"想热更新协议库"的开发者就要注意。

服务名查找键是 (端口, TCP/UDP) 二元组,这就是为什么同样的端口号 TCP 和 UDP 可能对应不同协议。

GUI 页面架构

iced 应用通常把状态收敛进一个大 Struct。Sniffnet 的页面状态包括:

Overview(总览页面):当前适配器的实时上下行速率、连接数、协议分布饼图、各协议字节占比。

Inspect(检查页面):表格列出所有检测到的连接,可按源/目的 IP、端口、协议过滤,点开单条看详情。

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

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


一次数据包的完整旅程

用一个具体场景把上面拆散的机制串起来:你启动 Sniffnet,选了 Wi-Fi 适配器,浏览器打开 https://example.com

  1. 你先设置了过滤条件,界面把 tcp dst port 443 交给 Capture::filter(),编译成内核 BPF 程序。
  2. 内核在网卡驱动层就拦下目标端口不是 443 的包,白费的用户空间拷贝为零。你的 HTTPS 流量通过。
  3. networking 线程从 pcap 读到这个 1500 字节左右的以太网帧,交给 parse_packets
  4. etherparse 剥掉以太网头拿到 IPv4 头,再剥 IP 层拿到 TCP 头,得到源/目的 IP、端口、协议类型、负载长度。
  5. parse_packets(目的端口 443, TCP) 键去 phf 哈希表里查一次,命中 https,把这包标成 HTTPS。
  6. 一个 ParsedPacket 被塞进 async-channel。抓包线程立刻回去读下一个包,不等界面。
  7. GUI 线程从通道取出,更新 Overview 页的速率曲线和连接表;同一条连接累加字节数,窗口计数加一。
  8. iced 收到 Message,触发一次重绘。你看到速率曲线向上跳了一下。
  9. 如果 GeoIP 数据库里有目标 IP,maxminddb 查一次国家并缓存;Inspect 页那行连接旁边就多了一面国旗。

这一步一步里,真正"抓"的部分在第 2、3 步,剩下的全是在把抓到的数据变成"人看得懂的连接"。这也是理解 Sniffnet 架构的关键——大部分代码不是抓包,而是围绕抓包结果做的整理与呈现。


性能优化策略

Release 编译优化

Cargo.toml 的 release 配置直接反映了对性能的认真程度:

[profile.release]
opt-level = 3    # 最高优化级别
lto = true       # 跨 crate 链接期优化
strip = true     # 剥离调试符号
codegen-units = 1 # 单 codegen 单元,换取更多优化空间

lto = true 让编译器能跨 crate 边界做内联和死代码消除。以 pcap 为例,未做 LTO 时调用 pcap 函数有一层函数跳转开销;开启后这些调用能被内联进调用点,减少单包处理路径上的间接跳转。codegen-units = 1 会拉长编译时间,但对性能敏感的桌面应用是划算的交换。

异步并发:channel 而非锁

抓包和渲染之间用 async-channel 而不是标准库同步 mpsc。它的价值在于抓包线程永远不阻塞:缓冲区满时它可以选择丢弃或给界面信号,而不是让抓包停下等界面消费。对实时监控,丢几帧老数据远好过界面拖慢抓包。

内存守恒

Snaplen 设成 65535,是 pcap 允许的最大单包捕获长度,保证任何帧(包括巨型帧)都有充足缓冲。对绝大多数不到 1500 字节的以太网帧,这不算浪费——缓冲是复用的一块固定内存,不是每包重新申请。

流量统计用滑动窗口,只保留最近 N 分钟的数据点,超窗数据直接丢弃。连接表也按生命周期裁剪,已结束的连接移出活跃表。这样长时间运行,内存曲线保持平坦,不会线性上涨。


跨平台差异处理

跨平台是 Sniffnet 复杂度最高的地方,也是最容易看得出设计功力的部分。抓包读包逻辑三端共用,差异被压在"获取权限"“程序识别"这两个点上。

在 v1.5 之前,“这个连接是哪个程序发的"只在 Windows 上可查(因为它能从系统连接表拿到进程归属),另两端无从判断。v1.5 把程序识别扩展到了 Windows、Linux、macOS 甚至 BSD——各端通过不同的内部机制拿到进程信息:Windows 走系统连接表 API(netstat -ano 的数据源),Linux 借助 netlink 套接字读取连接对应的进程,macOS 则遍历系统打开的 socket 到进程的映射。三端用不同的底层来源,对外暴露的是同一套能力:在 Inspect 页看"这条连接是 Chrome 发的”。

Windows

Windows 抓包底层用 npcap(libpcap 的 Windows 移植),需要管理员权限。安装包为 MSI,支持静默安装和组策略批量部署。

进程归属的信息来源是系统连接表 API:据"本地端口"反查"拥有该连接的进程 PID”,再把这个 PID 填进 ParsedPacket.process_id

macOS

macOS 同样依赖 libpcap,抓包需要以授权后的权限运行(首次会提示授权)。安装包为 DMG。程序的进程识别通过遍历系统 socket 到进程的映射获得。

Linux

Linux 抓包也依赖 libpcap,但提供了最灵活的权限方案。RPM/DEB 安装后可用 setcap 给二进制授予 CAP_NET_RAW,让普通用户也能抓包:

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

进程识别走 netlink 套接字读取连接与进程的对应关系。Linux 还支持 AppImage——把运行时、库、资源打成一个可执行文件,不污染系统。

三端权限与能力差异总结:

平台底层库权限方式程序识别来源
Windowsnpcap管理员权限系统连接表 API
macOSlibpcap授权提示socket → 进程遍历
Linuxlibpcapsetcap capabilitiesnetlink 套接字

开发与扩展

运行调试版本

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

首次编译需装系统依赖:Linux 要 libpcap-dev,macOS 要 Xcode 命令行工具,Windows 要 Visual Studio Build Tools。详见仓库 Wiki 的 Required Dependencies。

新增协议识别

想在运行时新增服务映射,改 services.txt 加一行:

# 格式:服务名 <Tab> 端口/传输协议
myapp  8101/tcp

注意:改动后必须重新编译,因为这张表在编译期被烘焙成哈希表,并受条目数断言约束。若你只是加条目,把 build.rs 里的断言一并更新,不然编译会失败。

自定义主题

内置 Deep Cosmos、Monokai、Dracula 等主题。新建主题修改 gui/styles/ 下的定义文件,遵循 iced 的颜色系统即可。


常见问题与故障排查

Q: 运行 sniffnet 时提示 “Permission denied” 怎么办?

Linux:设置 capabilities:

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

macOS:以授权方式运行,首次启动配合系统授权提示。

Windows:以管理员身份运行。

Q: 为什么看不到任何流量?

按顺序排查:

  1. 适配器选择:是否在 UI 里选了正确的适配器(通常是 Wi-Fi 或 Ethernet)。
  2. 过滤器设置:BPF 过滤是否过严,先清空过滤器再试。
  3. 权限问题:确认已正确配置权限。
  4. 网络活动:所选适配器上是否有真实的出流量。

Q: 为什么看不到"是哪个程序发的"?

程序识别在 v1.5 起覆盖 Windows、macOS、Linux(乃至 BSD),但各端条件略有差别:

  • Windows:以管理员身份运行,稍等片刻让系统连接表建立出进程映射。
  • macOS:授予抓包相关权限后可用。
  • Linux:授予 CAP_NET_RAW 后走 netlink 读取连接对应的进程。

自测题

  1. Sniffnet 为什么选 Rust + iced?Rust 带来了哪些实际好处?
  2. Sniffnet 的两条主线和它们之间的边界各是什么?
  3. BPF 过滤器在哪一层起作用?如何设置只捕获 HTTPS 流量?
  4. services.txt 为什么是编译期的?运行时改成无效吗?
  5. 在 Linux 上让非 root 用户运行 Sniffnet 需要做什么?程序识别在 v1.5 前/后有何不同?
参考答案

题 1:Rust 提供内存安全、异步并发(tokio)、静态分发。iced 提供单向数据流、GPU 加速、跨平台控件。

题 2:抓包链路(网卡 → pcap → 解析)和展示链路(聚合 → 图表 → 渲染),中间用 async-channel 边界解耦,抓包线程不被界面阻塞。

题 3:双在内核层。BPF 过滤程序由内核执行,不匹配的包不进用户空间。tcp dst port 443 只留 HTTPS 出流量。

题 4:因为 6000+ 条映射在 build.rs 里编译成 phf 静态哈希表,运行时是 O(1) 查找。运行时改 services.txt 不会生效,需要重新编译(且要同步更新条目数断言)。

题 5:Linux 用 setcap cap_net_raw,cap_net_admin=eip 授予能力;程序识别在 v1.5 前仅 Windows 能做(查系统连接表),v1.5 起扩展到 Windows、macOS、Linux 与 BSD,各端用不同来源(系统连接表 API / socket 遍历 / netlink)拿到进程归属。


进阶路径

按下面顺序读,每环都搭在前一环的问题上:

  1. Sniffnet GitHub 仓库:先通读 README 和 Wiki 的 Required Dependencies,建立整体认知。
  2. pcap crate 文档:想搞懂"如何抓包"“BPF 怎么工作”,这是最直接的参考。
  3. iced 官方教程:想理解 State-View-Message 模型、如何在界面里嵌实时图表时读。当前依赖是 iced 0.14。
  4. etherparse crate 文档:需要自定义协议解析时读。
  5. plotters-iced2 文档(可选):想基于 Sniffnet 做二次开发或复现实时图表时读。

与 Wireshark 如何取舍

把 Sniffnet 定位清楚,才知道什么时候派得上用场。

Wireshark 胜在深度:完整解码器、协议分析、流重组、复杂过滤表达式,是安全分析和协议调试的标配。它的问题是学习曲线陡,界面信息密度高,普通用户被劝退。

Sniffnet 赢在直觉:三端图形界面 + 进程归属 + 地理定位 + 一键通知,是"我想知道电脑在和谁通信"这类问题的答案,装了就能用。

一个可行的采用顺序:

  • 日常查流量、看谁在联网、简单告警 → 直接上 Sniffnet,几分钟上手。
  • 排查协议问题、深挖会话、取证 → 该用 Wireshark,Sniffnet 导出 PCAP 正好喂给 Wireshark 继续分析。
  • 两者不冲突,Sniffnet 抓包、Wireshark 深挖,是目前被验证过的高效组合。

项目信息:

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

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

参与讨论

使用 GitHub 登录。欢迎补充事实、异议与实践。