@@ -115,6 +115,7 @@ const RULE_BLOCK_CN_FET_STRICT: u32 = 1 << 3; // FET 严格模式(默认阻止
115115const RULE_BLOCK_CN_WIREGUARD : u32 = 1 << 4 ;
116116const RULE_BLOCK_CN_ALL : u32 = 1 << 5 ;
117117const RULE_BLOCK_CN_FET_LOOSE : u32 = 1 << 6 ; // FET 宽松模式(默认放过)
118+ const RULE_BLOCK_CN_QUIC : u32 = 1 << 7 ;
118119
119120// WireGuard 协议常量
120121const 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) ]
0 commit comments