[Bug] REJECT outbound silently drops UDP packets (nopPacketConn) instead of rejecting, behaving identically to REJECT-DROP
Author: 4ekuct25Created Sep 14, 2026Updated Sep 15, 2026
Verify steps
- Ensure you are using the latest version of Mihomo or Mihomo Alpha from this repository.
- I have searched on the issue tracker for a related issue.
- I have read the documentation and was unable to solve the issue.
- This is an issue of the Mihomo core per se, not to the derivatives of Mihomo, like OpenMihomo or KoolMihomo.
Mihomo version
Mihomo v1.19.27 / Alpha (all current versions)
What OS are you seeing the problem on?
Linux, macOS
Mihomo config
rules:
- AND,((DST-PORT,443),(NETWORK,UDP)),REJECT
# or
- RULE-SET,quic,REJECTMihomo log
[Rule] 192.168.1.100:54321 --> 157.240.253.174:443 match And(DstPort(443), Network(UDP)) using REJECTDescription
In Mihomo, rules matching UDP traffic with target REJECT are commonly used to block QUIC / HTTP/3 traffic so that clients (web browsers, mobile apps like Instagram, YouTube, etc.) instantly fall back to TCP (HTTP/2).
However, in adapter/outbound/reject.go:
func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata, ...) (C.PacketConn, error) {
return &nopPacketConn{}, nil
}
type nopPacketConn struct{}
func (npc *nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return len(b), nil
}
func (npc *nopPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
return 0, nil, io.EOF
}And in adapter/outbound/reject_drop.go:
func (r *RejectDrop) ListenPacketContext(ctx context.Context, metadata *C.Metadata, ...) (C.PacketConn, error) {
return &nopPacketConn{}, nil
}Both REJECT and REJECT-DROP use the exact same nopPacketConn, which accepts UDP packets and discards them silently (WriteTo returns len(b), nil).
The Problem:
- For TCP,
REJECTactively resets/closes the connection, giving the client an immediate connection reset (RST / ECONNRESET). - For UDP,
REJECTbehaves as a silent black hole (100% identical toREJECT-DROP). - When a client sends UDP packets (e.g. QUIC ClientHello to port 443), it receives no response, no ICMP Port Unreachable, and no error.
- As a result, the client's network stack waits for the UDP socket timeout (typically 2 to 5 seconds per connection attempt) before falling back to TCP. In media-heavy apps (like Instagram Reels or video streaming), every new media segment initiates a QUIC attempt, causing visible 2–5 second freezes and buffering delays.
Expected Behavior:
REJECT should actively reject UDP packets rather than silently dropping them:
- When traffic is routed to
REJECT(e.g. in TUN mode or TPROXY), reply with ICMP Port Unreachable (IPv4: Type 3, Code 3 / IPv6: Type 1, Code 4) back to the client, or actively signal connection refusal. - This would differentiate
REJECTfromREJECT-DROPfor UDP (matching their TCP distinction) and allow instant (<5 ms) TCP fallback when blocking QUIC.
Source: MetaCubeX/mihomo