From 80ea96e5b4f200efb3377572f8c27f200a91315a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 24 Jul 2026 10:26:49 +0800 Subject: [PATCH] Reject connections to redirect listener --- redirect_iptables.go | 22 ++++++++++++++++ redirect_nftables.go | 11 ++++++++ redirect_nftables_rules.go | 53 ++++++++++++++++++++++++++++++++++++++ redirect_server.go | 25 +++++++++++++----- 4 files changed, 104 insertions(+), 7 deletions(-) diff --git a/redirect_iptables.go b/redirect_iptables.go index d918363..1b7948a 100644 --- a/redirect_iptables.go +++ b/redirect_iptables.go @@ -44,6 +44,24 @@ func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error { if err != nil { return err } + if r.redirectServer != nil { + tableNameInput := r.tableName + "-input" + err = r.runShell(iptablesPath, "-t filter -N", tableNameInput) + if err != nil { + return err + } + err = r.runShell(iptablesPath, "-t filter -A", tableNameInput, + "-p tcp --dport", redirectPort, + "-m conntrack ! --ctstate DNAT", + "-j REJECT --reject-with tcp-reset") + if err != nil { + return err + } + err = r.runShell(iptablesPath, "-t filter -I INPUT -j", tableNameInput) + if err != nil { + return err + } + } return nil } @@ -58,10 +76,14 @@ func (r *autoRedirect) cleanupIPTables() { func (r *autoRedirect) cleanupIPTablesForFamily(iptablesPath string) { tableNameOutput := r.tableName + "-output" + tableNameInput := r.tableName + "-input" _ = r.runShell(iptablesPath, "-t nat -D OUTPUT -j", tableNameOutput) _ = r.runShell(iptablesPath, "-t nat -F", tableNameOutput) _ = r.runShell(iptablesPath, "-t nat -X", tableNameOutput) + _ = r.runShell(iptablesPath, "-t filter -D INPUT -j", tableNameInput) + _ = r.runShell(iptablesPath, "-t filter -F", tableNameInput) + _ = r.runShell(iptablesPath, "-t filter -X", tableNameInput) } func (r *autoRedirect) runShell(commands ...any) error { diff --git a/redirect_nftables.go b/redirect_nftables.go index 266bbe9..fc09ec4 100644 --- a/redirect_nftables.go +++ b/redirect_nftables.go @@ -123,6 +123,17 @@ func (r *autoRedirect) setupNFTables() error { } } + if r.redirectServer != nil { + chainInput := nft.AddChain(&nftables.Chain{ + Name: "input", + Table: table, + Hooknum: nftables.ChainHookInput, + Priority: nftables.ChainPriorityFilter, + Type: nftables.ChainTypeFilter, + }) + r.nftablesCreateRedirectPortReject(nft, table, chainInput) + } + chainPreRouting := nft.AddChain(&nftables.Chain{ Name: "prerouting", Table: table, diff --git a/redirect_nftables_rules.go b/redirect_nftables_rules.go index dddd9c6..d5b10f2 100644 --- a/redirect_nftables_rules.go +++ b/redirect_nftables_rules.go @@ -613,6 +613,59 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft return nil } +// IPS_DST_NAT in linux/netfilter/nf_conntrack_common.h, not exported by golang.org/x/sys +const conntrackStatusDstNAT = 1 << 5 + +func (r *autoRedirect) nftablesCreateRedirectPortReject(nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain) { + nft.AddRule(&nftables.Rule{ + Table: table, + Chain: chain, + Exprs: []expr.Any{ + &expr.Meta{ + Key: expr.MetaKeyL4PROTO, + Register: 1, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_TCP}, + }, + &expr.Payload{ + OperationType: expr.PayloadLoad, + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 2, + Len: 2, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: binaryutil.BigEndian.PutUint16(r.redirectPort()), + }, + &expr.Ct{ + Key: expr.CtKeySTATUS, + Register: 1, + }, + &expr.Bitwise{ + SourceRegister: 1, + DestRegister: 1, + Len: 4, + Mask: binaryutil.NativeEndian.PutUint32(conntrackStatusDstNAT), + Xor: make([]byte, 4), + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: make([]byte, 4), + }, + &expr.Counter{}, + &expr.Reject{ + Type: unix.NFT_REJECT_TCP_RST, + }, + }, + }) +} + func (r *autoRedirect) nftablesCreateMark(nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain) { nft.AddRule(&nftables.Rule{ Table: table, diff --git a/redirect_server.go b/redirect_server.go index 7590b35..1f138ec 100644 --- a/redirect_server.go +++ b/redirect_server.go @@ -54,22 +54,33 @@ func (s *redirectServer) Close() error { } func (s *redirectServer) loopIn() { + var retryDelay time.Duration for { conn, err := s.listener.AcceptTCP() if err != nil { - var netError net.Error - //nolint:staticcheck - if errors.As(err, &netError) && netError.Temporary() { - s.logger.Error(err) - continue - } if s.inShutdown.Load() && E.IsClosed(err) { return } + var netError net.Error + //nolint:staticcheck + if errors.As(err, &netError) && netError.Temporary() { + if retryDelay == 0 { + retryDelay = 5 * time.Millisecond + } else { + retryDelay *= 2 + } + if retryDelay > time.Second { + retryDelay = time.Second + } + s.logger.Error("accept: ", err, ": retrying in ", retryDelay) + time.Sleep(retryDelay) + continue + } s.listener.Close() s.logger.Error("serve error: ", err) - continue + return } + retryDelay = 0 source := M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap() destination, err := control.GetOriginalDestination(conn) if err != nil {