Fix flow close race

This commit is contained in:
世界 2026-07-21 14:48:16 +08:00
parent b59636919c
commit e5c21070ae
No known key found for this signature in database
GPG key ID: CD109927C34A63C4

View file

@ -3,6 +3,7 @@ package tun
import ( import (
"maps" "maps"
"net/netip" "net/netip"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -113,6 +114,7 @@ type ForwardDispatcher struct {
logger logger.Logger logger logger.Logger
udpTimeout time.Duration udpTimeout time.Duration
icmpTimeout time.Duration icmpTimeout time.Duration
access sync.RWMutex
table map[flowKey]*flowEntry table map[flowKey]*flowEntry
lastSweep int64 lastSweep int64
@ -168,26 +170,41 @@ func (d *ForwardDispatcher) Close() {
return return
} }
d.returnPath.closed.Store(true) d.returnPath.closed.Store(true)
d.access.Lock()
flows := make([]*forwardFlow, 0, len(d.table))
for _, entry := range d.table { for _, entry := range d.table {
if entry.flow != nil { if entry.flow != nil {
entry.flow.close(FlowCloseReset) flows = append(flows, entry.flow)
} }
} }
ports := make([]Port, 0, len(d.ports))
for port, nat := range d.ports { for port, nat := range d.ports {
if nat != nil { if nat != nil {
port.DetachReturn(&d.returnPath) ports = append(ports, port)
} }
} }
d.access.Unlock()
for _, flow := range flows {
flow.close(FlowCloseReset)
}
for _, port := range ports {
port.DetachReturn(&d.returnPath)
}
} }
func (d *ForwardDispatcher) Dispatch(packet []byte) bool { func (d *ForwardDispatcher) Dispatch(packet []byte) bool {
if d == nil { if d == nil || d.returnPath.closed.Load() {
return false return false
} }
parsed, ok := parseForwardPacket(packet) parsed, ok := parseForwardPacket(packet)
if !ok || parsed.fragment || !parsed.hasFlow { if !ok || parsed.fragment || !parsed.hasFlow {
return false return false
} }
d.access.RLock()
defer d.access.RUnlock()
if d.returnPath.closed.Load() {
return false
}
key := parsed.flowKey() key := parsed.flowKey()
now := d.now() now := d.now()
entry, loaded := d.table[key] entry, loaded := d.table[key]
@ -553,7 +570,12 @@ func (d *ForwardDispatcher) ResetNetwork() {
} }
func (d *ForwardDispatcher) Flush() { func (d *ForwardDispatcher) Flush() {
if d == nil { if d == nil || d.returnPath.closed.Load() {
return
}
d.access.RLock()
defer d.access.RUnlock()
if d.returnPath.closed.Load() {
return return
} }
if d.resetPending.Swap(false) { if d.resetPending.Swap(false) {