draft: Tun respond ICMP port unreachable when rejecting UDP packets

This commit is contained in:
H1JK
2026-01-01 06:48:36 +08:00
parent 1f8bee9710
commit 58c4110b57
9 changed files with 100 additions and 16 deletions

View File

@@ -34,7 +34,10 @@ func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
if err := r.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
return newPacketConn(&nopPacketConn{}, r), nil
if r.drop {
return newPacketConn(dropPacketConn{}, r), nil
}
return newPacketConn(nopPacketConn{}, r), C.ErrResetByRule
}
func (r *Reject) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
@@ -90,12 +93,10 @@ func NewPass() *Reject {
type nopConn struct{}
func (rw nopConn) Read(b []byte) (int, error) { return 0, io.EOF }
func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { return io.EOF }
func (rw nopConn) Write(b []byte) (int, error) { return 0, io.EOF }
func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF }
func (rw nopConn) Read(b []byte) (int, error) { return 0, C.ErrResetByRule }
func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { return C.ErrResetByRule }
func (rw nopConn) Write(b []byte) (int, error) { return 0, C.ErrResetByRule }
func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { return C.ErrResetByRule }
func (rw nopConn) Close() error { return nil }
func (rw nopConn) LocalAddr() net.Addr { return nil }
func (rw nopConn) RemoteAddr() net.Addr { return nil }
@@ -110,11 +111,9 @@ 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) (int, net.Addr, error) {
return 0, nil, io.EOF
}
func (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, C.ErrResetByRule }
func (npc nopPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) {
return nil, nil, nil, io.EOF
return nil, nil, nil, C.ErrResetByRule
}
func (npc nopPacketConn) Close() error { return nil }
func (npc nopPacketConn) LocalAddr() net.Addr { return udpAddrIPv4Unspecified }
@@ -137,3 +136,18 @@ func (rw dropConn) RemoteAddr() net.Addr { return nil }
func (rw dropConn) SetDeadline(time.Time) error { return nil }
func (rw dropConn) SetReadDeadline(time.Time) error { return nil }
func (rw dropConn) SetWriteDeadline(time.Time) error { return nil }
type dropPacketConn struct{}
func (npc dropPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return len(b), nil
}
func (npc dropPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, io.EOF }
func (npc dropPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) {
return nil, nil, nil, io.EOF
}
func (npc dropPacketConn) Close() error { return nil }
func (npc dropPacketConn) LocalAddr() net.Addr { return udpAddrIPv4Unspecified }
func (npc dropPacketConn) SetDeadline(time.Time) error { return nil }
func (npc dropPacketConn) SetReadDeadline(time.Time) error { return nil }
func (npc dropPacketConn) SetWriteDeadline(time.Time) error { return nil }

View File

@@ -33,6 +33,9 @@ type WriterWithUpstream = network.WriterWithUpstream
type WithUpstreamWriter = network.WithUpstreamWriter
type WithUpstream = common.WithUpstream
type HandshakeSuccess = network.HandshakeSuccess
type HandshakeFailure = network.HandshakeFailure
var UnwrapReader = network.UnwrapReader
var UnwrapWriter = network.UnwrapWriter

31
common/utils/cast.go Normal file
View File

@@ -0,0 +1,31 @@
package utils
import (
"fmt"
"net"
)
type WithUpstream interface {
Upstream() any
}
type stdWithUpstreamNetConn interface {
NetConn() net.Conn
}
func Cast[T any](obj any) (_ T, _ bool) {
if c, ok := obj.(T); ok {
fmt.Printf("Got 1: %T\n", obj) // TODO
return c, true
}
if u, ok := obj.(WithUpstream); ok {
fmt.Printf("Upstream 2: %T\n", obj) // TODO
return Cast[T](u.Upstream())
}
if u, ok := obj.(stdWithUpstreamNetConn); ok {
fmt.Printf("Std 3: %T\n", obj) // TODO
return Cast[T](u.NetConn())
}
fmt.Printf("Failed: %T\n", obj) // TODO
return
}

View File

@@ -274,6 +274,10 @@ func (s *packetAdapter) Key() string {
return s.key
}
func (s *packetAdapter) Upstream() any {
return s.UDPPacket
}
func NewPacketAdapter(packet UDPPacket, metadata *Metadata) PacketAdapter {
return &packetAdapter{
packet,

View File

@@ -1,5 +1,11 @@
package constant
import (
"io"
"github.com/metacubex/sing/common/exceptions"
)
// Rule Type
const (
Domain RuleType = iota
@@ -129,3 +135,7 @@ type RuleGroup interface {
Rule
GetRecodeSize() int
}
var (
ErrResetByRule = exceptions.Cause(io.EOF, "reset by rule") // TODO: replace function from sing
)

2
go.mod
View File

@@ -35,7 +35,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.7
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.11
github.com/metacubex/sing-tun v0.4.12-0.20251231220427-f11396db2fa1
github.com/metacubex/sing-vmess v0.2.4
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1

4
go.sum
View File

@@ -133,8 +133,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6w
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.11 h1:NG5zpvYPbBXf+9GSUmDaGCDwl3hZXV677tbRAw0QtCM=
github.com/metacubex/sing-tun v0.4.11/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-tun v0.4.12-0.20251231220427-f11396db2fa1 h1:eaQCkCI2STxaGiVK35huV9TjUu3QDo3LiS/LKO5n1Z8=
github.com/metacubex/sing-tun v0.4.12-0.20251231220427-f11396db2fa1/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=

View File

@@ -296,8 +296,19 @@ func (c *packet) LocalAddr() net.Addr {
func (c *packet) Drop() {
c.buff.Release()
// always try to report success to ensure that the memory is freed up
if handshake, isHandshake := common.Cast[network.HandshakeSuccess](c); isHandshake {
_ = handshake.HandshakeSuccess()
}
}
func (c *packet) InAddr() net.Addr {
return c.lAddr
}
func (c *packet) Upstream() any {
if c.writer == nil {
return nil
}
return *c.writer
}

View File

@@ -463,9 +463,17 @@ func handleUDPConn(packet C.PacketAdapter) {
rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) {
return proxy.ListenPacketContext(ctx, dialMetadata)
}, func(err error) {
logMetadataErr(metadata, rule, proxy, err)
if !errors.Is(err, C.ErrResetByRule) {
logMetadataErr(metadata, rule, proxy, err)
}
})
if err != nil {
if errors.Is(err, C.ErrResetByRule) {
logMetadata(metadata, rule, rawPc)
if handshake, isHandshake := utils.Cast[N.HandshakeFailure](packet); isHandshake {
_ = handshake.HandshakeFailure(err)
}
}
return nil, nil, err
}
logMetadata(metadata, rule, rawPc)
@@ -490,7 +498,7 @@ func handleUDPConn(packet C.PacketAdapter) {
sender.Process(pc, proxy)
}()
}
sender.Send(packet) // nonblocking
sender.Send(packet) // will not block
}
func handleTCPConn(connCtx C.ConnContext) {
@@ -700,6 +708,9 @@ func shouldStopRetry(err error) bool {
if errors.Is(err, resolver.ErrIPv6Disabled) {
return true
}
if errors.Is(err, C.ErrResetByRule) {
return true
}
if errors.Is(err, loopback.ErrReject) {
return true
}