惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
月光博客
月光博客
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
O
OpenAI News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Know Your Adversary
Know Your Adversary
Last Week in AI
Last Week in AI
S
Securelist
Engineering at Meta
Engineering at Meta
博客园 - 司徒正美
P
Privacy & Cybersecurity Law Blog
T
Tailwind CSS Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
U
Unit 42
Microsoft Azure Blog
Microsoft Azure Blog
Hacker News: Ask HN
Hacker News: Ask HN
Hugging Face - Blog
Hugging Face - Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
P
Proofpoint News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
Lohrmann on Cybersecurity
IT之家
IT之家
Security Archives - TechRepublic
Security Archives - TechRepublic
I
InfoQ
S
Security @ Cisco Blogs
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Full Disclosure
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
人人都是产品经理
人人都是产品经理
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
B
Blog RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research

鸟窝

鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 LLM 究竟是如何工作的? Go 实验特性详解 amd64 微架构级别对 Go 程序性能提升多少? Loop Engineering 实践:一次批量实现 8 个 issue,完成夔牛工具的开发 Loop Engineering 实践:我把 RDMA 开发库移植到 Go 语言,花费 239 块钱 傻瓜式RDMA高性能网络开发:从零跑到 400 Gb每秒 百度物理网络监控工具开源第二弹:毫秒级监控工具 baize,让你的网络问题无处遁形 百度网络监控工具开源第三弹:lidar — 不只是 pingmesh 别只盯着gopacket了,看看这个强大的网络库 大厂的内部工具居然开源了! 一窥百度物理网络秒级监控定位的秘密 如何构建你自己的 Agent 运行时 寻找你代码中的臭味:一个让 AI 帮你嗅出架构腐化的开源 Skill 套壳不丢人!我用Go+AI搓了一个Agent统一编排框架,ClaudeCode-Codex-Pi全被我包了 告别死锁和陈旧语法、告别性能瓶颈:三个开源 Skill,新手Gopher 秒变 Go 语言大神 SPEC和PRD的区别 SPEC和方案设计有什么区别 从需求到上线,让 AI 管理你的整个研发流程! antigravity-cli 使用 CLIProxyAPI, 让最新的 Codex 能够支持国内的各大模型 Clawpatch + codex-review:AI 代码审查工具链的正确打开方式 拆解Manus:沙盒架构深度解析 40+ Claude Code Tips: From Basics to Advanced Ralph 实验:构建 SQLite UI 了解 Manus Sandbox - 您的云计算机 Notex:一个开源 NotebookLM 替代方案的实现 拆解Manus:真正有用的深度报告的生成 Claude Code 使用 我给每个模型服务商『捐』了10块钱,只为了... Go之禅 - 基于Rob Pike思想的Go语言哲学 Codex CLI vs Gemini CLI vs Claude Code:谁是最佳选择? goskills:Claude Skills 功能强大,为我所用 一行代码使用 Claude Skill 和 deepseek langchain + MCP:如虎添翼 Linux 中网络包的一生 godotenv 库介绍 Codex CLI vs Gemini CLI vs Claude Code: Which is the Best? 使用Linux 30年了,我都不知道 ping 8.8 还能这么用? 从 AI 哪里挣钱? Go sync 包近两年发展综述 deepseek-v3.2-exp的闪电索引器
鸟窝
by smallnest · 2026-06-30 · via 鸟窝

这是百度网络监控工具 nettools 开源系列的第四篇。前三篇分别介绍了 bitflip/baize(UDP 丢包与改包检测工具和Agent)、lidar(TCP SYN 端口可达性探测),它们解决的都是「服务器之间」「点到点」的探测问题——前提是:探测机和被探测对象,至少有一端在我们手里。

但有一类设备,我们既无法在它上面装 agent,也没法在它对面的机房里放一台探测机。这就是今天的主角 evr 要解决的困境。

这也是我更深入的了解网络包的构造,把网络探测玩出花了来,对我的网络编程的功力大增的一个很好的场景。

项目地址:https://github.com/baidu/nettools
文档:https://nettools.rpcx.io

一、evr 探测的困境:探测机进不去客户机房

先说一个真实的场景。

百度有大量的云客户,我们提供的 EVR(Edge Virtual Router,边缘虚拟路由器) 设备作为客户侧网络接入百度云网络的边界节点。EVR 往上连百度的骨干/城域网络,往下连客户自己的虚拟网络(VXLAN overlay)。

EVR - 边缘虚拟路由器,通常用于在虚拟化环境中实现路由功能。EVR 位于网络的边缘,用于连接内部网络和外部网络(如客户机房)。

现在问题来了:我们需要监控「百度网络 → EVR」这一段链路的健康度——有没有丢包、延迟多大、有没有改包。按照前几个工具的套路,我们的方案应该是:

  • 在 EVR 设备上装个 agent?—— 不行。EVR 是网络设备/客户侧设备,我们没有权限往里塞监控程序。
  • 在 EVR 对面(客户机房内)放一台探测机,做点到点探测?—— 更不行。那是客户的机房,正常情况我们不可能在客户的物理环境里申请一台探测机常驻。

lidar 那一套「发 SYN,靠对端内核 TCP 协议栈自动回 SYN-ACK/RST」的思路,在这里也不灵——EVR 不是一台服务器,它不会帮你跑 TCP 协议栈三次握手,或者说不允许我们高频的探测。

1
2
3
4
5
6
   百度侧                          边缘设备
┌──────────┐ ┌──────────────────────┐
│ 探测机 │ ───── ??? ─────► │ EVR 设备 │
│ (我们的) │ │ (装不了 agent) │
└──────────┘ │ 对面也放不了探测机 │
└──────────────────────┘

困境的本质是:被探测对象不可控,且它对面也无法部署探测机。 我们需要一个 「单边」 就能完成的探测方案——只在百度侧放一台机器,让 EVR 设备自己「帮我们把包送回来」。

答案藏在 EVR 设备的工作原理里:它是一个 VXLAN VTEP(VXLAN Tunnel End Point)。而 VTEP 有一个非常好用的特性——它会忠实地按照内层 IP 头转发解封后的内层帧。这就给了我们「构造一个会被反射回来的 VXLAN 包」的可能。

要理解这个技巧,得先看懂 VXLAN 的包结构。

二、VXLAN:把二层帧塞进 UDP 里

VXLAN(Virtual Extensible LAN,RFC 7348)是数据中心 overlay 网络的事实标准。它要解决的核心问题是:传统 VLAN 只有 12 位 VLAN ID,最多 4096 个二层网络,在大规模多租户云环境里完全不够用。

VXLAN 的做法简单粗暴又有效:把一个完整的二层以太网帧,整个塞进一个 UDP 数据报里,通过三层网络传输。这样原本受限于物理二层域的网络,可以跨越任意三层网络延展,VNI(VXLAN Network Identifier)有 24 位,支持约 1600 万个 overlay 网络。

它的包结构从外到内是这样的:

一层层拆开看:

大小 关键字段 作用
外层 Ethernet 14 B 物理 MAC underlay 二层转发
外层 IP 20 B src / dst IP underlay 三层路由(VTEP 之间)
外层 UDP 8 B dport = 4789 VXLAN 标准端口,VTEP 据此识别
VXLAN header 8 B flags + VNI(24 位) 标识 overlay 网络
内层 Ethernet 14 B 租户 MAC 被封装的原始二层帧开始
内层 IP 20 B 内层 src / dst overlay 里的真实通信地址
内层 UDP/TCP 8 B 内层端口 租户的真实流量
Payload N B 业务数据 原始负载
其中 8 字节的 VXLAN header 结构是:
1
2
3
4
5
6
7
 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|R|R|R|I|R|R|R| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| VXLAN Network Identifier (VNI) | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

I 位(第 5 位)置 1 表示 VNI 有效,剩下的 24 位就是 VNI。

VTEP 的工作模型:它从外层 UDP/4789 收到一个 VXLAN 包,剥掉外层 Eth/IP/UDP 和 VXLAN header,拿到内层的原始以太网帧,然后按内层 IP 头继续转发

注意最后这句话——「按内层 IP 头继续转发」。这就是 evr 整个设计的命门所在:如果我把内层 dst IP 填成我自己,VTEP 解封后不就把包转回给我了吗?

三、evr 工具:让 EVR 自己把包反射回来

实现原理:自环内层帧 + payload 内嵌 EVR 源 IP

evr 的核心是一个反直觉但极其精简的设计——「自环内层帧」。

先看一个绕不开的硬约束:VTEP 是按内层 IP 头转发解封后的帧的。所以要让反射的包能回到探测机,内层 dst IP 就必须填探测机本机——填别的地址,包就被转到别处去了,根本回不来。这一点没得选。

真正有选择空间的是内层 src IP。最「自然」的想法是:把每个目标 EVR 的源 IP 填进内层 src IP,回包时靠这个 src IP 区分「是哪个目标反射回来的」。多目标时,本机收到一堆 dst=本机、src=各个 EVR 的回包,按 src IP 分流即可。

但 evr 偏偏没这么做——它把内层 src IP 也填成了本机(src = dst = 本机,自环)。这样一来,那个本可用来标识目标的内层 src IP 就被「浪费」掉了,回包里全是 src=本机, dst=本机,彼此长得一模一样,没法区分目标了。

那为什么要主动放弃这个天然的标识位?这里其实有一个技巧
当出现故障的时候,我们要使用traceroute的功能,通过设置TTL,回获取内层的回包经过的路径。通过设置src IP为探测机的IP,我们就能够让ICMP的回包发送给探测机,这样就可以把回程的路径都探测出来了。

evr 的三招合起来是这样:

第一招:内层 src IP 和 dst IP 都填探测机的本机地址。

1
2
3
4

inner, err := codec.EncodeVxlanInner(a.conf.VNI, a.conf.SrcMAC, a.conf.DstMAC,
p.innerDstIP, p.innerDstIP,
newPort, a.conf.InnerDstPort, uint8(a.conf.TOS), a.conf.TTL, payload)

EVR 解封 VXLAN 后看到内层 dst = 本机,自然就把内层帧转回本机。本机的 raw socket 直接收到回包,不需要在对端起任何 server,也不需要单独申请回包用的 IP/端口

第二招:把「真正的目标标识」嵌进 payload。

既然内层 src/dst 都被占用成本机了,那「这个包是探测哪个 EVR 的」靠什么区分?答案是把目标 EVR 的IP 写进 payload 的第 24~28 字节:

1
2
3
4
5
6






回包时解析 payload 里的这 4 字节,一步定位回目标:

1
2
3

seq, ts, evrIP := codec.DecodeWithSrcIP(payload)
p := a.peerByEVRSrc[evrIP.String()]

本质上,evr 把「目标标识」从 IP 头搬到了 payload 里,让 EVR 的 VXLAN 反射动作天然变成一次「单源对多目标」的回包匹配。代价仅仅是 4 字节 payload + 解析时多读 4 字节。

第三招:外层源 IP 可以 spoof。

evr 用 ipv4.NewRawConn 包了一层(等价于开启 IP_HDRINCL),让内核不再自己生成外层 IP 头,而是原样发出我们手工拼好的外层 IPv4 头。这样外层 src IP 可以填成 mock_src——一个虚假的IP地址。当然这个功能是可选的。

这有什么好处呢?
为了在Evr上识别出来探测包,我们其实是在Evr中做了一些特殊的配置的,使用和客户不同的VNI,识别出『探测机』的源IP,有不同的路由策略。但这也带来的一个问题: 如果探测机有故障,需要切换探测机的时候,需要在Evr上改配置。
为线上的网络设备修改配置流程上很复杂,需要评审改配方案、需要等待变更窗口,需要在凌晨的时候操作等。所以不能及时的更改探测机。但是如果我们使用mockIP,那么就可以分分钟的把探测切到新的探测机上,不需要修改Evr的配置。这也是我们的一个技巧。

完整的一发一收流程:

1
2
3
4
5
6
7
8
9
10
   发送侧                                  回程侧
┌────────────────────────────┐ ┌────────────────────────────┐
│ 外层 IP : mock_src → vtepIP │ │ 内层 dst = 本机 │
│ 外层 UDP: srcPort → 4789 │ 反射 │ ↓ 本机 raw socket 收到 │
│ ┌ VXLAN(VNI) ───────────┐ │ ────► │ 解析 payload[24:28] │
│ │ 内层 ETH/IPv4/UDP │ │ │ 得到 EVR 源 IP │
│ │ src=本机, dst=本机 │ │ │ peerByEVRSrc[ip] → target │
│ │ payload: ...+EVR srcIP│ │ │ 累加 stat(丢包/延迟/翻转) │
│ └───────────────────────┘ │ └────────────────────────────┘
└────────────────────────────┘

整个过程只在百度侧部署一台 evr,EVR 设备本身充当了反射器——困境破解。

使用方法

evr 由 JSON 配置驱动,每个 target 用 vtep#evrSrc[#mockSrc] 三段式表达:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"id": "evr-probe-1",
"client_addr": "203.0.113.10",
"targets": "198.51.100.96#192.0.2.1#203.0.113.99,198.51.100.97#192.0.2.2#203.0.113.99",
"vni": 15990000,
"tos": 64,
"ttl": 64,
"client_port_range": "63000,63999",
"rate_in_span": 2000,
"span": "1s",
"delay": "5s",
"msg_len": 1024,
"log_dir": "./log",
"log_max_age_days": 3
}

三段式的含义:

必填 含义
vtepIP EVR VTEP 的 IP(外层目的地址)
evrSrcIP 「目标」标识,嵌入 payload 用于回包匹配
mockSrcIP 外层源 IP;为空则用 client_addr
启动:
1
2
3
4
5
6
7
8
9
10
11

sudo ./evr -c /etc/evr/evr.json


sudo ./evr \
--client-addr 203.0.113.10 \
--targets 198.51.100.96#192.0.2.1#203.0.113.99 \
--rate-in-span 2000 --span 1s --msg-len 1024


sudo ./evr -c /etc/evr/evr.json --rate-in-span 5000 --verbose

evr 需要 sudo(或 CAP_NET_RAW)来创建 raw socket、启用 IP_HDRINCL、设置 IP TOS 并挂载 BPF。它仅在 Linux 上有意义,macOS 只能用于编译开发。

四、evr 中的技巧与高频 FAQ

evr 看似简单,但藏了几个值得拿出来单独说的工程技巧。

技巧 1:BPF 内核层过滤,别让无关 UDP 流量打扰

读 socket 用的是 ip4:udp raw socket,它默认会收到本机所有 UDP 报文。在万兆网卡、几十万 pps 的探测机上,内核把海量无关包拷到用户态,性能直接崩。

解法是在内核层用 cBPF 过滤,只放行三个条件同时命中的包:

1
2
3
(1) IPv4 协议号 = UDP (17)
(2) IPv4 TOS = cfg.tos
(3) UDP 目的端口 = inner_dst_port (默认 8972)

只有这三条都满足才投递到用户态,几乎零开销。而写 socket 上则反过来装一个「全丢」的 BPF——阻止内核给这个发送 socket 排队任何回包。

技巧 2:源端口轮转覆盖 ECMP 多路径

和 bitflip/lidar 一样,evr 也面临多路径覆盖问题。两个固定地址之间,五元组固定则 ECMP 哈希结果不变,永远只走一条链路。evr 通过 client_port_range 配置一段源端口,每发一轮就让 srcPort + 1,在统计意义上覆盖 ECMP 哈希全空间。

技巧 3:4 种 Salt 模式抓 bitflip

payload 第 28 字节往后是 Salt 填充,按 seq % 4 在四种模式间轮换,专门用来检测链路上的位翻转:

  • 0xFF 全 1 —— 暴露任何「1 变 0」的翻转
  • 0x00 全 0 —— 暴露任何「0 变 1」的翻转
  • 0x5A 交替位 (01011010) —— 适配 NIC 串行链路的奇偶错误
  • 互补 0xAAAA / 0x5555 —— 专治 1's complement 校验和漏检的「互补翻转」,这是普通 UDP/TCP checksum 唯一无法察觉的一类 bitflip

回包时如果 payload 长度等于发送长度,就和对应 Salt 比对,命中差异即记一条 [client bitflip] 日志。这套 Salt 实现与 baize/kuiniu 完全共用。

高频 FAQ

Q:mock_src 是怎么生效的?内核为什么不会把它改回去?
evr 在写端用 ipv4.NewRawConn(conn) 包了一层,等价于开启 IP_HDRINCL。开启后内核不再生成自己的 IP 头,而是原样发出我们手工拼的外层 IPv4 头,其中 src 就是 mock_src。如果不包这一层,内核会前置一份自己的 IP 头,导致双层 IP 封装且 mock_src 失效。

Q:rate_in_span 是单 target 速率还是总速率?
所有 target 的总速率。比如 12 个 target、rate_in_span=2000/s,每个 target 平均只有约 166 pps。要提高单 target 速率,就减少 target 数量或调大 rate_in_span

Q:为什么内层 src 和 dst 都填本机 IP?
这样 EVR 反射回来的内层帧 dst 就是本机,本机 raw socket 直接收得到,无需在对端起 server、无需单独申请回包 IP。这是 evr 破解「对面进不去」困境的核心招式。src IP也填写本机IP是为了定位的时候traceroute的需要。

Q:为什么 evr 必须用 sudo?
需要创建 ip4:udp raw socket、启用 IP_HDRINCL、设置 IP TOS/DSCP 并挂载 BPF。Linux 上需要 CAP_NET_RAW,最简单就是 sudo 或 systemd 以 root 启动。

Q:和 baize / kuiniu 怎么选?
普通业务网络长期监控用 baize;AI 训练的 GPU NIC 互联(RoCE)用 kuiniu;机房 VXLAN/EVR 路径与 EVR 设备本身的探测用 evr——关键区别是 evr 不需要在对端起 server,EVR 设备本身就是反射器。


evr 把一个看似无解的困境——「探测机进不去客户机房,被探测设备又装不了 agent」——通过对 VXLAN VTEP 反射特性的巧妙利用,变成了一个单边即可完成的探测。这背后是同一套技术栈在不同网络场景下的复用:raw socket 构造报文、BPF 内核过滤、源端口轮转覆盖 ECMP、Salt 检测 bitflip、时间桶统计。

这只是 nettools 的冰山一角。后续还有网关设备监控、定位工具,以及巨量监控数据的处理方案。

项目地址:https://github.com/baidu/nettools

欢迎 Star、试用、提 Issue 和 PR。