mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-08-03 22:24:44 +00:00
usbip: cancel driver reattach attempts on session teardown
PLUGIN_HARDWARE_ONCE only suppresses retries of the initial attach; when an established connection later drops, wsk_receive.cpp unconditionally schedules background reattach attempts (~20 tries over ~2 hours) toward this session's dead one-shot loopback port. With ephemeral port reuse those ghost connects steal a later session's Accept and fail its Plugin. Wire up STOP_ATTACH_ATTEMPTS (0x805, present in both bundled driver builds) and cancel the session's exact host/service/busid location before plugout.
This commit is contained in:
parent
33d6f60d88
commit
7df951e82c
3 changed files with 60 additions and 6 deletions
|
|
@ -101,6 +101,35 @@ func (c *Controller) Plugin(host, service, busid string) (int, error) {
|
|||
return port, nil
|
||||
}
|
||||
|
||||
// StopAttachAttempts cancels the driver's scheduled background
|
||||
// reconnect attempts for the given location (the exact host/service/
|
||||
// busid triple passed to Plugin), or for every location when all three
|
||||
// are empty. Returns the number of canceled requests. Must be called
|
||||
// on session teardown: the dropped connection's reattach attempts
|
||||
// target a one-shot loopback port that no longer exists and would
|
||||
// otherwise steal a later session's Accept.
|
||||
func (c *Controller) StopAttachAttempts(host, service, busid string) (int, error) {
|
||||
var buf [stopAttachAttemptsSize]byte
|
||||
binary.LittleEndian.PutUint32(buf[offsetPluginSize:], stopAttachAttemptsSize)
|
||||
err := putCString(buf[offsetPluginBusID:offsetPluginBusID+BusIDSize], busid)
|
||||
if err != nil {
|
||||
return 0, E.Cause(err, "usbipvhci: busid")
|
||||
}
|
||||
err = putCString(buf[offsetPluginService:offsetPluginService+serviceSize], service)
|
||||
if err != nil {
|
||||
return 0, E.Cause(err, "usbipvhci: service")
|
||||
}
|
||||
err = putCString(buf[offsetPluginHost:offsetPluginHost+hostSize], host)
|
||||
if err != nil {
|
||||
return 0, E.Cause(err, "usbipvhci: host")
|
||||
}
|
||||
_, err = c.ioctl(ioctlStopAttachAttempts, buf[:], buf[:])
|
||||
if err != nil {
|
||||
return 0, E.Cause(err, "usbipvhci: STOP_ATTACH_ATTEMPTS")
|
||||
}
|
||||
return int(int32(binary.LittleEndian.Uint32(buf[offsetStopAttachAttemptsCount:]))), nil
|
||||
}
|
||||
|
||||
// Plugout detaches the device on the given hub port. PortAll detaches
|
||||
// every device. A port the driver already tore down (after its socket
|
||||
// dropped) reports STATUS_DEVICE_NOT_CONNECTED, surfaced as an error.
|
||||
|
|
|
|||
|
|
@ -21,11 +21,16 @@ const (
|
|||
|
||||
// IOCTL codes: CTL_CODE(FILE_DEVICE_UNKNOWN, function, METHOD_BUFFERED,
|
||||
// FILE_READ_DATA|FILE_WRITE_DATA). Function values < 0x800 are reserved
|
||||
// for Microsoft. PLUGIN_HARDWARE_ONCE attaches without the driver's own
|
||||
// auto-reconnect — sing-box owns reconnection at the service layer.
|
||||
// for Microsoft. PLUGIN_HARDWARE_ONCE only suppresses retries of the
|
||||
// initial attach (vhci_ioctl.cpp checks one_attempt in the connect
|
||||
// completion alone); when an established connection later drops,
|
||||
// wsk_receive.cpp unconditionally schedules reattach attempts toward
|
||||
// the by-then-dead one-shot loopback port, so every teardown must
|
||||
// cancel them via STOP_ATTACH_ATTEMPTS.
|
||||
const (
|
||||
ioctlPluginHardwareOnce uint32 = 0x0022_E018 // function 0x806
|
||||
ioctlPlugoutHardware uint32 = 0x0022_E004 // function 0x801
|
||||
ioctlStopAttachAttempts uint32 = 0x0022_E014 // function 0x805
|
||||
)
|
||||
|
||||
// Field offsets within usbip::vhci::ioctl::plugin_hardware, which is
|
||||
|
|
@ -48,6 +53,15 @@ const pluginHardwareSize = 1100
|
|||
// plugoutHardwareSize is sizeof(plugout_hardware): ULONG size + int port.
|
||||
const plugoutHardwareSize = 8
|
||||
|
||||
// stopAttachAttemptsSize is sizeof(ioctl::stop_attach_attempts):
|
||||
// base + imported_device_location (1097) padded to 1100 for the
|
||||
// trailing `int count` (OUT), total 1104. Present in both bundled
|
||||
// driver versions; absent before 0.9.7.5.
|
||||
const (
|
||||
stopAttachAttemptsSize = 1104
|
||||
offsetStopAttachAttemptsCount = 1100
|
||||
)
|
||||
|
||||
// PortAll detaches every imported device (PORT_ALL = -1).
|
||||
const PortAll = -1
|
||||
|
||||
|
|
|
|||
|
|
@ -90,8 +90,9 @@ type windowsClientSession struct {
|
|||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
listener net.Listener
|
||||
hubPort int
|
||||
listener net.Listener
|
||||
relayService string // loopback port passed to Plugin, for StopAttachAttempts
|
||||
hubPort int
|
||||
|
||||
connAccess sync.Mutex
|
||||
driverConn net.Conn
|
||||
|
|
@ -132,7 +133,8 @@ func (s *windowsClientSession) start(ctx context.Context) error {
|
|||
// Plugin blocks until the driver has connected to the loopback
|
||||
// listener and the import handshake (run by acceptAndRelay) completed.
|
||||
port := listener.Addr().(*net.TCPAddr).Port
|
||||
hubPort, err := s.controller.Plugin(loopbackHost, strconv.Itoa(port), s.info.BusIDString())
|
||||
s.relayService = strconv.Itoa(port)
|
||||
hubPort, err := s.controller.Plugin(loopbackHost, s.relayService, s.info.BusIDString())
|
||||
if err != nil {
|
||||
s.cancel()
|
||||
s.markDone()
|
||||
|
|
@ -344,8 +346,17 @@ func (s *windowsClientSession) Start() error {
|
|||
|
||||
func (s *windowsClientSession) Close() error {
|
||||
s.closeOnce.Do(func() {
|
||||
// If the connection already dropped, the driver has scheduled
|
||||
// background reattach attempts toward this session's dead
|
||||
// loopback port; cancel them before detaching.
|
||||
count, err := s.controller.StopAttachAttempts(loopbackHost, s.relayService, s.info.BusIDString())
|
||||
if err != nil {
|
||||
s.logger.Debug("usbip windows: stop attach attempts: ", err)
|
||||
} else if count > 0 {
|
||||
s.logger.Debug("usbip windows: canceled ", count, " driver reattach attempts")
|
||||
}
|
||||
if s.hubPort > 0 {
|
||||
err := s.controller.Plugout(s.hubPort)
|
||||
err = s.controller.Plugout(s.hubPort)
|
||||
if err != nil {
|
||||
s.logger.Debug("usbip windows: plugout port ", s.hubPort, ": ", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue