Skip to content

Commit a340fb6

Browse files
committed
feat: block quic
1 parent e486c34 commit a340fb6

8 files changed

Lines changed: 173 additions & 30 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ Rust Firewall - 基于 eBPF/XDP 的高性能防火墙,支持协议深度检测
2222
3. **屏蔽中国 IP 的 SOCKS5 入站** - 使用协议深度检测识别 SOCKS5 流量,阻止来自中国 IP 的 SOCKS5 入站连接
2323
4. **屏蔽中国 IP 的全加密流量入站** - 使用 FET 算法识别 Shadowsocks、V2Ray 等加密代理,阻止来自中国 IP 的全加密代理流量
2424
5. **屏蔽中国 IP 的 WireGuard VPN 入站** - 精准识别 WireGuard VPN 协议,阻止来自中国 IP 的 WireGuard 流量
25-
6. **屏蔽中国 IP 的所有入站流量** - 阻止所有来自中国 IP 的入站连接(不限协议、不限端口)
25+
6. **屏蔽中国 IP 的 QUIC 入站** - 精准识别 QUIC 协议(HTTP/3),阻止来自中国 IP 的 QUIC 流量
26+
7. **屏蔽中国 IP 的所有入站流量** - 阻止所有来自中国 IP 的入站连接(不限协议、不限端口)
2627

2728
### 协议深度检测 (DPI)
2829

@@ -32,6 +33,7 @@ Rust Firewall - 基于 eBPF/XDP 的高性能防火墙,支持协议深度检测
3233
- **SOCKS5 检测**:识别 SOCKS5 握手协议特征
3334
- **全加密流量检测**:使用统计算法识别 Shadowsocks、V2Ray 等加密代理
3435
- **WireGuard 检测**:精准识别 WireGuard VPN 协议
36+
- **QUIC 检测**:识别 QUIC v1/v2 和 Google QUIC 协议(HTTP/3)
3537
- **不依赖端口号**:即使服务运行在非标准端口也能识别
3638

3739
### 性能特性
@@ -76,10 +78,11 @@ sudo ./target/release/rfw --iface eth0 \
7678
--block-cn-http \
7779
--block-cn-socks5 \
7880
--block-cn-fet-strict \
79-
--block-cn-wg
81+
--block-cn-wg \
82+
--block-cn-quic
8083

8184
# 启用详细日志
82-
sudo RUST_LOG=info ./target/release/rfw --iface eth0 --block-cn-wg
85+
sudo RUST_LOG=info ./target/release/rfw --iface eth0 --block-cn-wg --block-cn-quic
8386
```
8487

8588
## 使用说明
@@ -162,7 +165,32 @@ sudo ./target/release/rfw --iface eth0 --block-cn-wg
162165
- 误报率极低,性能开销小
163166
- 不会影响其他 UDP 应用
164167

165-
#### 6. 屏蔽中国 IP 的所有入站流量
168+
#### 6. 屏蔽中国 IP 的 QUIC 入站
169+
170+
```bash
171+
sudo ./target/release/rfw --iface eth0 --block-cn-quic
172+
```
173+
174+
阻止来自中国 IP 的 QUIC 协议流量(HTTP/3)
175+
176+
**特点:**
177+
- 精准识别 QUIC v1(RFC 9000)和 QUIC v2(RFC 9369)
178+
- 支持检测 Google QUIC 协议
179+
- 识别长头部和短头部数据包
180+
- 误报率极低,不影响其他 UDP 应用
181+
182+
**支持的 QUIC 版本:**
183+
- QUIC v1(RFC 9000,版本号 0x00000001)
184+
- QUIC v2(RFC 9369,版本号 0x6b3343cf)
185+
- Google QUIC(Q0xx 系列)
186+
- 版本协商包
187+
188+
**使用场景:**
189+
- 阻止基于 HTTP/3 的服务被滥用
190+
- 防止 QUIC 代理服务器被滥用
191+
- 限制新一代加密传输协议的入站连接
192+
193+
#### 7. 屏蔽中国 IP 的所有入站流量
166194

167195
```bash
168196
sudo ./target/release/rfw --iface eth0 --block-cn-all
@@ -193,13 +221,15 @@ sudo ./target/release/rfw --iface eth0 \
193221
--block-cn-socks5 \
194222
--block-cn-fet-strict \
195223
--block-cn-wg \
224+
--block-cn-quic
196225

197226
# 仅启用 GeoIP 相关规则
198227
sudo ./target/release/rfw --iface eth0 \
199228
--block-cn-http \
200229
--block-cn-socks5 \
201230
--block-cn-fet-strict \
202-
--block-cn-wg
231+
--block-cn-wg \
232+
--block-cn-quic
203233

204234
# 仅启用协议检测规则(HTTP + SOCKS5)
205235
sudo ./target/release/rfw --iface eth0 \
@@ -210,10 +240,19 @@ sudo ./target/release/rfw --iface eth0 \
210240
sudo ./target/release/rfw --iface eth0 \
211241
--block-cn-fet-strict
212242

243+
# 仅启用 VPN/代理协议检测(WireGuard + QUIC)
244+
sudo ./target/release/rfw --iface eth0 \
245+
--block-cn-wg \
246+
--block-cn-quic
247+
213248
# 仅启用 WireGuard VPN 检测
214249
sudo ./target/release/rfw --iface eth0 \
215250
--block-cn-wg
216251

252+
# 仅启用 QUIC 协议检测
253+
sudo ./target/release/rfw --iface eth0 \
254+
--block-cn-quic
255+
217256
# 仅启用屏蔽所有中国 IP 入站(最简单直接)
218257
sudo ./target/release/rfw --iface eth0 \
219258
--block-cn-all
@@ -260,7 +299,7 @@ rfw 基于 **eBPF/XDP** 技术,使用 **Rust** 语言开发。
260299
2. 检查是否为 IPv4 数据包(**目前仅支持 IPv4**
261300
3. 根据启用的规则进行检测:
262301
- GeoIP 检测:检查源 IP 是否属于中国
263-
- 协议检测:识别 HTTP、SOCKS5、WireGuard 等协议特征
302+
- 协议检测:识别 HTTP、SOCKS5、WireGuard、QUIC 等协议特征
264303
- 端口检测:识别 Email 发送端口
265304
4. 返回判决:允许通过或丢弃
266305

rfw-common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rfw-common"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
edition.workspace = true
55

66
license.workspace = true

rfw-common/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub const RULE_BLOCK_CN_FET_STRICT: u32 = 1 << 3; // 屏蔽中国 IP 的全加
1515
pub const RULE_BLOCK_CN_WIREGUARD: u32 = 1 << 4; // 屏蔽中国 IP 的 WireGuard 入站
1616
pub const RULE_BLOCK_CN_ALL: u32 = 1 << 5; // 屏蔽中国 IP 的所有入站流量
1717
pub const RULE_BLOCK_CN_FET_LOOSE: u32 = 1 << 6; // 屏蔽中国 IP 的全加密流量入站 (宽松模式,默认放过)
18+
pub const RULE_BLOCK_CN_QUIC: u32 = 1 << 7; // 屏蔽中国 IP 的 QUIC 入站
1819

1920
impl FirewallConfig {
2021
pub fn new() -> Self {

rfw-ebpf/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rfw-ebpf"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
edition.workspace = true
55

66
[dependencies]

rfw-ebpf/src/main.rs

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ const RULE_BLOCK_CN_FET_STRICT: u32 = 1 << 3; // FET 严格模式(默认阻止
115115
const RULE_BLOCK_CN_WIREGUARD: u32 = 1 << 4;
116116
const RULE_BLOCK_CN_ALL: u32 = 1 << 5;
117117
const RULE_BLOCK_CN_FET_LOOSE: u32 = 1 << 6; // FET 宽松模式(默认放过)
118+
const RULE_BLOCK_CN_QUIC: u32 = 1 << 7;
118119

119120
// WireGuard 协议常量
120121
const WG_TYPE_HANDSHAKE_INIT: u8 = 1;
@@ -247,8 +248,7 @@ fn try_rfw(ctx: XdpContext) -> Result<u32, ()> {
247248
let payload_size = end.saturating_sub(start + payload_offset);
248249

249250
// 没有payload,跳过检测(例如纯ACK包、FIN包等控制包)
250-
// 小包豁免:payload < 20 字节的包暂时放过
251-
if payload_size == 0 || payload_size < 20 {
251+
if payload_size == 0 {
252252
return Ok(xdp_action::XDP_PASS);
253253
}
254254

@@ -316,8 +316,8 @@ fn try_rfw(ctx: XdpContext) -> Result<u32, ()> {
316316
}
317317

318318
// 检查 FET(全加密流量)入站屏蔽规则
319-
// FET 检测需要至少 6 字节
320-
if !should_block && payload_size >= 6 {
319+
// FET 检测需要至少 16 字节
320+
if !should_block && payload_size >= 16 {
321321
// 判断模式:严格或宽松
322322
let strict_mode = (*config_flags & RULE_BLOCK_CN_FET_STRICT) != 0;
323323
let loose_mode = (*config_flags & RULE_BLOCK_CN_FET_LOOSE) != 0;
@@ -346,7 +346,10 @@ fn try_rfw(ctx: XdpContext) -> Result<u32, ()> {
346346
}
347347
}
348348
}
349-
349+
// 放过小包等后续大包再检查
350+
if payload_size <= 6 {
351+
return Ok(xdp_action::XDP_PASS);
352+
}
350353
// 处理检测结果
351354
if should_block {
352355
// 标记这个连接为已阻止,后续包也会被DROP
@@ -371,19 +374,34 @@ fn try_rfw(ctx: XdpContext) -> Result<u32, ()> {
371374
// 检查 WireGuard 入站屏蔽规则(来自中国的入站)
372375
if (*config_flags & RULE_BLOCK_CN_WIREGUARD) != 0 {
373376
// 通过协议深度检测识别 WireGuard 流量
374-
if is_wireguard_packet(&ctx, payload_offset)? {
375-
if is_cn_ip(src_ip)? {
376-
let src_port = u16::from_be(unsafe { (*udp_hdr).source });
377-
let dst_port = u16::from_be(unsafe { (*udp_hdr).dest });
378-
info!(
379-
&ctx,
380-
"BLOCKED: WireGuard VPN 入站流量来自中国, 源 {:i}:{} -> 目标端口 {}",
381-
u32::from_be(src_ip),
382-
src_port,
383-
dst_port
384-
);
385-
return Ok(xdp_action::XDP_DROP);
386-
}
377+
if is_cn_ip(src_ip)? && is_wireguard_packet(&ctx, payload_offset)? {
378+
let src_port = u16::from_be(unsafe { (*udp_hdr).source });
379+
let dst_port = u16::from_be(unsafe { (*udp_hdr).dest });
380+
info!(
381+
&ctx,
382+
"BLOCKED: WireGuard VPN 入站流量来自中国, 源 {:i}:{} -> 目标端口 {}",
383+
u32::from_be(src_ip),
384+
src_port,
385+
dst_port
386+
);
387+
return Ok(xdp_action::XDP_DROP);
388+
}
389+
}
390+
391+
// 检查 QUIC 入站屏蔽规则(来自中国的入站)
392+
if (*config_flags & RULE_BLOCK_CN_QUIC) != 0 {
393+
// 通过协议深度检测识别 QUIC 流量
394+
if is_cn_ip(src_ip)? && is_quic_packet(&ctx, payload_offset)? {
395+
let src_port = u16::from_be(unsafe { (*udp_hdr).source });
396+
let dst_port = u16::from_be(unsafe { (*udp_hdr).dest });
397+
info!(
398+
&ctx,
399+
"BLOCKED: QUIC 入站流量来自中国, 源 {:i}:{} -> 目标端口 {}",
400+
u32::from_be(src_ip),
401+
src_port,
402+
dst_port
403+
);
404+
return Ok(xdp_action::XDP_DROP);
387405
}
388406
}
389407
}
@@ -865,6 +883,74 @@ fn is_wireguard_packet(ctx: &XdpContext, payload_offset: usize) -> Result<bool,
865883
Ok(false)
866884
}
867885

886+
// 检测是否为 QUIC 协议数据包
887+
// QUIC 协议特征:
888+
// - 长头部包(Long Header):首字节最高位为 1,包含版本号字段
889+
// - QUIC v1: 版本号 0x00000001
890+
// - QUIC v2: 版本号 0x6b3343cf
891+
// - 版本协商包: 版本号 0x00000000
892+
// - 短头部包(Short Header):首字节最高位为 0(已建立连接)
893+
#[inline(always)]
894+
fn is_quic_packet(ctx: &XdpContext, payload_offset: usize) -> Result<bool, ()> {
895+
// 读取前5个字节(首字节 + 4字节版本号)
896+
let header = match ptr_at::<[u8; 5]>(ctx, payload_offset) {
897+
Ok(ptr) => unsafe { *ptr },
898+
Err(_) => return Ok(false),
899+
};
900+
901+
let first_byte = header[0];
902+
903+
// 检查长头部包(最高位为 1)
904+
if (first_byte & 0x80) == 0x80 {
905+
// 读取版本号(大端序)
906+
let version = ((header[1] as u32) << 24)
907+
| ((header[2] as u32) << 16)
908+
| ((header[3] as u32) << 8)
909+
| (header[4] as u32);
910+
911+
// 检查常见的 QUIC 版本号
912+
// 0x00000000: 版本协商包
913+
// 0x00000001: QUIC v1 (RFC 9000)
914+
// 0x6b3343cf: QUIC v2 (RFC 9369)
915+
// 0x51303xxx: Google QUIC (Q0xx)
916+
if version == 0x00000000
917+
|| version == 0x00000001
918+
|| version == 0x6b3343cf
919+
|| (version & 0xFFFF0000) == 0x51303000
920+
{
921+
return Ok(true);
922+
}
923+
924+
// 其他小版本号也可能是 QUIC(允许未来版本)
925+
// 但为了减少误判,只识别已知版本
926+
if version != 0 && version < 0x0000000a {
927+
return Ok(true);
928+
}
929+
} else {
930+
// 短头部包(最高位为 0)
931+
// 这些是已建立连接的数据包,更难识别
932+
// 但如果首字节符合 QUIC 短头部格式,也认为是 QUIC
933+
// 短头部格式:0XXX XXXX (最高位为0)
934+
// 为了减少误判,我们只在有明确特征时才识别
935+
936+
// 检查是否有 QUIC 连接 ID(通过数据包长度判断)
937+
let start = ctx.data();
938+
let end = ctx.data_end();
939+
let packet_len = end.saturating_sub(start + payload_offset);
940+
941+
// QUIC 短头部包通常至少有 20 字节
942+
// 并且第一个字节的特定位模式
943+
if packet_len >= 20 {
944+
// 如果固定位(bit 6)为 1,这是 QUIC v1/v2 的要求
945+
if (first_byte & 0x40) == 0x40 {
946+
return Ok(true);
947+
}
948+
}
949+
}
950+
951+
Ok(false)
952+
}
953+
868954
// 辅助函数:从数据包中获取指定偏移的指针
869955
// CRITICAL: 必须使用直接的指针比较,verifier 才能理解
870956
#[inline(always)]

rfw/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rfw"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
edition.workspace = true
55

66
license.workspace = true

rfw/src/main.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ struct Opt {
166166
#[clap(long)]
167167
block_cn_wg: bool,
168168

169+
/// 屏蔽来自中国 IP 的 QUIC 协议入站连接
170+
///
171+
/// 检测 QUIC 协议特征:
172+
/// - QUIC v1 (RFC 9000)
173+
/// - QUIC v2 (RFC 9369)
174+
/// - Google QUIC
175+
///
176+
/// 用途: 阻止基于 QUIC 的服务(如 HTTP/3)被滥用
177+
#[clap(long)]
178+
block_cn_quic: bool,
179+
169180
/// 屏蔽来自中国 IP 的所有入站流量(不限协议)
170181
///
171182
/// 最激进的规则,直接在 IP 层阻止所有中国来源的入站连接
@@ -194,6 +205,7 @@ async fn main() -> anyhow::Result<()> {
194205
&& !opt.block_cn_fet_strict
195206
&& !opt.block_cn_fet_loose
196207
&& !opt.block_cn_wg
208+
&& !opt.block_cn_quic
197209
&& !opt.block_cn_all
198210
{
199211
println!("警告: 未启用任何防火墙规则,程序将运行但不执行任何过滤操作");
@@ -262,6 +274,10 @@ async fn main() -> anyhow::Result<()> {
262274
config_flags |= rfw_common::RULE_BLOCK_CN_WIREGUARD;
263275
info!("启用规则: 屏蔽中国 IP 的 WireGuard VPN 入站");
264276
}
277+
if opt.block_cn_quic {
278+
config_flags |= rfw_common::RULE_BLOCK_CN_QUIC;
279+
info!("启用规则: 屏蔽中国 IP 的 QUIC 入站");
280+
}
265281
if opt.block_cn_all {
266282
config_flags |= rfw_common::RULE_BLOCK_CN_ALL;
267283
info!("启用规则: 屏蔽中国 IP 的所有入站流量");
@@ -278,6 +294,7 @@ async fn main() -> anyhow::Result<()> {
278294
|| opt.block_cn_fet_strict
279295
|| opt.block_cn_fet_loose
280296
|| opt.block_cn_wg
297+
|| opt.block_cn_quic
281298
|| opt.block_cn_all
282299
{
283300
info!("检测到需要 GeoIP 规则,正在下载中国 IP 数据...");

0 commit comments

Comments
 (0)