Skip to content

Commit e66e266

Browse files
committed
Restore Windows --dev source selection
Keep Windows --dev usable by resolving the selected device to a source IP and clearing SourceDevice before the tracer reaches Windows send paths. Update docs and project notes to describe that Windows routing may still choose the actual egress interface.
1 parent e1115a2 commit e66e266

6 files changed

Lines changed: 29 additions & 24 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@
290290
- `--dev``cmd/cmd.go` 先解析网卡并推导 `srcAddr`(已处理非 `*net.IPNet` 地址类型,避免 panic)。
291291
- `trace.Config` 现在显式携带 `SourceDevice` / `DisableMPLS`,Darwin TCP/UDP 抓包与 MPLS 解析优先走会话级配置,不再依赖 Web 侧临时改写全局变量。
292292
- `trace.Config` 也显式携带 `Context``TracerouteWithContext(...)` 通过把上游 ctx 传入各 tracer 的 `signal.NotifyContext(...)` 基底,让 TCP/UDP fallback MTR 可以响应取消。
293-
- Windows TCP 目前仍无法把 `SourceDevice` 映射到 WinDivert 接口选择;当前策略是显式报错拒绝,而不是静默忽略该字段。
293+
- Windows 下 ICMP/TCP/UDP 的 `--dev` 都必须保持 v1.6.2 兼容行为:先解析指定网卡对应的 source IP,写入 `SrcAddr`,再清空 `SourceDevice`,让后续路径按 source address 继续运行;不要把 Windows `--dev` 改成拒绝执行或报错退出。
294+
- 这只是通过 source IP 影响 Windows 路由选择,不代表 WinDivert 或 socket 已支持真实按设备绑定;README 需要如实说明可能不准确。独立 `--mtu` 同样按 source-address 发送,但可额外保留 device 名用于本地 MTU 查询。
294295
- MTR 标题显示源信息来自:
295296
- `--source`(最高优先)
296297
- `--dev` 推导

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,7 @@ nexttrace --file /path/to/your/iplist.txt
333333
#### `NextTrace` already supports route tracing for specified Network Devices
334334
335335
On macOS and Linux, `--dev` binds the requested source interface.
336-
On Windows, `--dev` only selects the source address and does not guarantee the actual egress interface.
337-
`TCP + --dev` remains explicitly unsupported on Windows and returns an error.
336+
On Windows, `--dev` resolves the source IP from the selected device and uses that source address for ICMP/TCP/UDP probes; it does not bind WinDivert or sockets to a real egress interface, so Windows routing may still choose a different path. The standalone `--mtu` mode follows the same source-address behavior and also uses the device name for local MTU lookup.
338337
339338
```bash
340339
# Use eth0 network interface
@@ -818,8 +817,9 @@ Arguments:
818817
packets
819818
-D --dev Use the specified network device for
820819
explicit source selection. On Windows,
821-
this only chooses the source address and
822-
does not guarantee the egress interface
820+
this selects the device source address;
821+
routing may still choose the egress
822+
interface
823823
--listen Set listen address for web console (e.g.
824824
127.0.0.1:30080)
825825
--deploy Start the Gin powered web console

README_zh_CN.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,7 @@ nexttrace --file /path/to/your/iplist.txt
331331
#### `NextTrace` 已支持指定网卡进行路由跟踪
332332

333333
在 macOS 和 Linux 上,`--dev` 会绑定到指定源网卡。
334-
在 Windows 上,`--dev` 只用于选择 source address,不保证真实出接口。
335-
`TCP + --dev` 在 Windows 上仍然是显式不支持的,会直接报错。
334+
在 Windows 上,`--dev` 会从指定网卡解析 source IP,并用该 source address 发起 ICMP/TCP/UDP 探测;它不会把 WinDivert 或 socket 绑定到真实出接口,实际出口仍可能由 Windows 路由表决定。独立 `--mtu` 模式也遵循相同的 source-address 语义,并额外使用网卡名查询本地 MTU。
336335

337336
```bash
338337
# 请注意 Lite 版本此参数不能和快速测试联用,如有需要请使用 enhanced 版本
@@ -794,8 +793,9 @@ Arguments:
794793
packets
795794
-D --dev Use the specified network device for
796795
explicit source selection. On Windows,
797-
this only chooses the source address and
798-
does not guarantee the egress interface
796+
this selects the device source address;
797+
routing may still choose the egress
798+
interface
799799
--listen Set listen address for web console (e.g.
800800
127.0.0.1:30080)
801801
--deploy Start the Gin powered web console

cmd/cmd.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ func Execute() {
10921092
naliMode := registerNaliFlag(parser)
10931093
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source address src_addr for outgoing packets"})
10941094
srcPort := parser.Int("", "source-port", &argparse.Options{Help: "Use source port src_port for outgoing packets"})
1095-
srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the specified network device for explicit source selection. On Windows, this only chooses the source address and does not guarantee the egress interface; TCP + --dev is not supported"})
1095+
srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the specified network device for explicit source selection. On Windows, this selects the device source address; routing may still choose the egress interface"})
10961096

10971097
webFlags := registerWebUIFlags(parser)
10981098
deployListen := webFlags.deployListen
@@ -1298,7 +1298,7 @@ func Execute() {
12981298
fmt.Println(srcResolveErr)
12991299
os.Exit(1)
13001300
}
1301-
// NormalizeExplicitSourceConfig enforces explicit --source/--dev rules and unsupported combinations.
1301+
// NormalizeExplicitSourceConfig applies explicit --source/--dev selection rules.
13021302
sourceCfg, srcResolveErr := trace.NormalizeExplicitSourceConfig(trace.UDPTrace, trace.Config{
13031303
OSType: osType,
13041304
DstIP: ip,
@@ -1436,7 +1436,7 @@ func Execute() {
14361436
fmt.Println(srcResolveErr)
14371437
os.Exit(1)
14381438
}
1439-
// NormalizeExplicitSourceConfig enforces explicit --source/--dev rules and unsupported combinations.
1439+
// NormalizeExplicitSourceConfig applies explicit --source/--dev selection rules.
14401440
sourceCfg, srcResolveErr := trace.NormalizeExplicitSourceConfig(method, trace.Config{
14411441
OSType: osType,
14421442
DstIP: ip,

trace/source_config.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ func ResolveConfiguredSrcAddr(dstIP net.IP, srcAddr, srcDev string) (resolved st
120120
}
121121

122122
func NormalizeExplicitSourceConfig(method Method, config Config) (Config, error) {
123+
_ = method
124+
123125
config.SrcAddr = strings.TrimSpace(config.SrcAddr)
124126
config.SourceDevice = strings.TrimSpace(config.SourceDevice)
125127
explicitSource := config.SrcAddr != ""
@@ -130,9 +132,6 @@ func NormalizeExplicitSourceConfig(method Method, config Config) (Config, error)
130132
if config.SourceDevice == "" {
131133
return config, nil
132134
}
133-
if config.OSType == osTypeWindows && method == TCPTrace {
134-
return config, fmt.Errorf("source_device %q is not supported on Windows TCP traces", config.SourceDevice)
135-
}
136135
if config.OSType == osTypeWindows && explicitSource {
137136
config.SourceDevice = ""
138137
return config, nil

trace/source_config_test.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,14 @@ func TestNormalizeExplicitSourceConfigWindowsIgnoresDeviceWhenSourceExplicitForI
300300
}
301301
}
302302

303-
func TestNormalizeExplicitSourceConfigWindowsRejectsTCPSourceDevice(t *testing.T) {
303+
func TestNormalizeExplicitSourceConfigWindowsTCPResolvesDeviceToSourceAddress(t *testing.T) {
304304
restore := stubSourceDeviceResolver(t, func(device string) (*net.Interface, error) {
305-
t.Fatalf("ResolveSourceDevice should not be called for unsupported Windows TCP source_device")
306-
return nil, nil
305+
if device != "Ethernet0" {
306+
t.Fatalf("ResolveSourceDevice device = %q, want Ethernet0", device)
307+
}
308+
return &net.Interface{Name: device}, nil
307309
}, func(_ *net.Interface) ([]net.Addr, error) {
308-
return nil, nil
310+
return []net.Addr{&net.IPNet{IP: net.ParseIP("192.0.2.44"), Mask: net.CIDRMask(24, 32)}}, nil
309311
})
310312
defer restore()
311313

@@ -315,12 +317,15 @@ func TestNormalizeExplicitSourceConfigWindowsRejectsTCPSourceDevice(t *testing.T
315317
SourceDevice: "Ethernet0",
316318
}
317319

318-
_, err := NormalizeExplicitSourceConfig(TCPTrace, cfg)
319-
if err == nil {
320-
t.Fatal("NormalizeExplicitSourceConfig() error = nil, want unsupported Windows TCP source_device error")
320+
got, err := NormalizeExplicitSourceConfig(TCPTrace, cfg)
321+
if err != nil {
322+
t.Fatalf("NormalizeExplicitSourceConfig() error = %v", err)
323+
}
324+
if got.SrcAddr != "192.0.2.44" {
325+
t.Fatalf("SrcAddr = %q, want 192.0.2.44", got.SrcAddr)
321326
}
322-
if err.Error() != `source_device "Ethernet0" is not supported on Windows TCP traces` {
323-
t.Fatalf("err = %q, want unsupported Windows TCP source_device error", err.Error())
327+
if got.SourceDevice != "" {
328+
t.Fatalf("SourceDevice = %q, want empty", got.SourceDevice)
324329
}
325330
}
326331

0 commit comments

Comments
 (0)