fix(listener): configure all TUN addresses for dual-stack IPv6 on Windows

Only the first entry of config.Net was installed on the wintun adapter,
so dual-stack setups (e.g. net=192.168.123.1/24,fd::2/64) silently
dropped the IPv6 address and IPv6 traffic never worked. Iterate every
configured address and select the netsh context by family (ip for IPv4,
ipv6 for IPv6). A failing address is logged and skipped so one bad
entry no longer aborts the rest or triggers the listenLoop's infinite
recreate-retry. If no address can be configured, return an error
instead of a nil peer IP.

Refs: go-gost/gost#624
This commit is contained in:
ginuerzh
2026-06-22 19:43:22 +08:00
parent 6a0ce8914e
commit 4057ac54b8
2 changed files with 46 additions and 10 deletions
+23 -5
View File
@@ -52,8 +52,17 @@ func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.
}
}
if len(l.md.config.Net) > 0 {
ipNet := l.md.config.Net[0]
// Loop over every configured address so dual-stack (IPv4+IPv6) setups
// install both addresses, selecting the netsh context by family: "ip"
// for IPv4, "ipv6" for IPv6. A failing address is logged and skipped so
// one bad entry doesn't prevent the rest from being applied; the first
// successfully installed address is reported as the interface IP.
//
// Caveat: unlike Linux's netlink.AddrAdd, "netsh interface ip set
// address" replaces rather than adds, so only a single IPv4 address is
// supported (one IPv4 + one IPv6 works; multiple IPv4 addresses
// silently overwrite).
for _, ipNet := range l.md.config.Net {
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+
"source=static addr=%s mask=%s gateway=none",
name, ipNet.IP.String(), ipMask(ipNet.Mask))
@@ -65,10 +74,19 @@ func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
l.log.Errorf("%s: %v", cmd, er)
continue
}
ip = ipNet.IP
if ip == nil {
ip = ipNet.IP
}
}
// If not a single address could be configured, surface an error rather
// than returning a nil IP (and thus a nil peer address) to the caller.
if ip == nil && len(l.md.config.Net) > 0 {
err = fmt.Errorf("failed to configure any address on interface %s", name)
return
}
if err = l.addRoutes(name, l.md.config.Gateway); err != nil {
+23 -5
View File
@@ -56,8 +56,17 @@ func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.
}
}
if len(l.md.config.Net) > 0 {
ipNet := l.md.config.Net[0]
// Loop over every configured address so dual-stack (IPv4+IPv6) setups
// install both addresses, selecting the netsh context by family: "ip"
// for IPv4, "ipv6" for IPv6. A failing address is logged and skipped so
// one bad entry doesn't prevent the rest from being applied; the first
// successfully installed address is reported as the interface IP.
//
// Caveat: unlike Linux's netlink.AddrAdd, "netsh interface ip set
// address" replaces rather than adds, so only a single IPv4 address is
// supported (one IPv4 + one IPv6 works; multiple IPv4 addresses
// silently overwrite).
for _, ipNet := range l.md.config.Net {
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+
"source=static addr=%s mask=%s",
name, ipNet.IP.String(), ipMask(ipNet.Mask))
@@ -73,10 +82,19 @@ func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.
l.log.Debugf("%s: %s", cmd, windows.ByteSliceToString(output))
}
if er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
l.log.Errorf("%s: %v", cmd, er)
continue
}
ip = ipNet.IP
if ip == nil {
ip = ipNet.IP
}
}
// If not a single address could be configured, surface an error rather
// than returning a nil IP (and thus a nil peer address) to the caller.
if ip == nil && len(l.md.config.Net) > 0 {
err = fmt.Errorf("failed to configure any address on interface %s", name)
return
}
if err = l.addRoutes(name, l.md.config.Gateway); err != nil {