fix: dhcp not working on windows
This commit is contained in:
parent
9fdfc06f6e
commit
a08c894f41
6 changed files with 83 additions and 47 deletions
|
@ -14,5 +14,15 @@ func ListenDHCPClient(ctx context.Context, ifaceName string) (net.PacketConn, er
|
||||||
listenAddr = "255.255.255.255:68"
|
listenAddr = "255.255.255.255:68"
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialer.ListenPacket(ctx, "udp4", listenAddr, dialer.WithInterface(ifaceName), dialer.WithAddrReuse(true))
|
options := []dialer.Option{
|
||||||
|
dialer.WithInterface(ifaceName),
|
||||||
|
dialer.WithAddrReuse(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback bind on windows, because syscall bind can not receive broadcast
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
options = append(options, dialer.WithFallbackBind(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialer.ListenPacket(ctx, "udp4", listenAddr, options...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package dialer
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/iface"
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
|
@ -49,3 +50,52 @@ func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination
|
||||||
|
|
||||||
return nil, iface.ErrAddrNotFound
|
return nil, iface.ErrAddrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fallbackBindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
|
||||||
|
if !destination.IsGlobalUnicast() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
local := uint64(0)
|
||||||
|
if dialer.LocalAddr != nil {
|
||||||
|
_, port, err := net.SplitHostPort(dialer.LocalAddr.String())
|
||||||
|
if err == nil {
|
||||||
|
local, _ = strconv.ParseUint(port, 10, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, destination, int(local))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.LocalAddr = addr
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fallbackBindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string) (string, error) {
|
||||||
|
_, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
port = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
local, _ := strconv.ParseUint(port, 10, 16)
|
||||||
|
|
||||||
|
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, netip.Addr{}, int(local))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fallbackParseNetwork(network string, addr netip.Addr) string {
|
||||||
|
// fix fallbackBindIfaceToListenConfig() force bind to an ipv4 address
|
||||||
|
if !strings.HasSuffix(network, "4") &&
|
||||||
|
!strings.HasSuffix(network, "6") &&
|
||||||
|
addr.Unmap().Is6() {
|
||||||
|
network += "6"
|
||||||
|
}
|
||||||
|
return network
|
||||||
|
}
|
||||||
|
|
|
@ -5,55 +5,16 @@ package dialer
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
|
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
|
||||||
if !destination.IsGlobalUnicast() {
|
return fallbackBindIfaceToDialer(ifaceName, dialer, network, destination)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local := uint64(0)
|
func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, network, address string) (string, error) {
|
||||||
if dialer.LocalAddr != nil {
|
return fallbackBindIfaceToListenConfig(ifaceName, lc, network, address)
|
||||||
_, port, err := net.SplitHostPort(dialer.LocalAddr.String())
|
|
||||||
if err == nil {
|
|
||||||
local, _ = strconv.ParseUint(port, 10, 16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, destination, int(local))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dialer.LocalAddr = addr
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func bindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string) (string, error) {
|
|
||||||
_, port, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
port = "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
local, _ := strconv.ParseUint(port, 10, 16)
|
|
||||||
|
|
||||||
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, netip.Addr{}, int(local))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return addr.String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseNetwork(network string, addr netip.Addr) string {
|
func ParseNetwork(network string, addr netip.Addr) string {
|
||||||
// fix bindIfaceToListenConfig() force bind to an ipv4 address
|
return fallbackParseNetwork(network, addr)
|
||||||
if !strings.HasSuffix(network, "4") &&
|
|
||||||
!strings.HasSuffix(network, "6") &&
|
|
||||||
addr.Unmap().Is6() {
|
|
||||||
network += "6"
|
|
||||||
}
|
|
||||||
return network
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,11 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
|
||||||
|
|
||||||
lc := &net.ListenConfig{}
|
lc := &net.ListenConfig{}
|
||||||
if cfg.interfaceName != "" {
|
if cfg.interfaceName != "" {
|
||||||
addr, err := bindIfaceToListenConfig(cfg.interfaceName, lc, network, address)
|
bind := bindIfaceToListenConfig
|
||||||
|
if cfg.fallbackBind {
|
||||||
|
bind = fallbackBindIfaceToListenConfig
|
||||||
|
}
|
||||||
|
addr, err := bind(cfg.interfaceName, lc, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -125,7 +129,11 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
|
||||||
|
|
||||||
dialer := netDialer.(*net.Dialer)
|
dialer := netDialer.(*net.Dialer)
|
||||||
if opt.interfaceName != "" {
|
if opt.interfaceName != "" {
|
||||||
if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil {
|
bind := bindIfaceToDialer
|
||||||
|
if opt.fallbackBind {
|
||||||
|
bind = fallbackBindIfaceToDialer
|
||||||
|
}
|
||||||
|
if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ type NetDialer interface {
|
||||||
|
|
||||||
type option struct {
|
type option struct {
|
||||||
interfaceName string
|
interfaceName string
|
||||||
|
fallbackBind bool
|
||||||
addrReuse bool
|
addrReuse bool
|
||||||
routingMark int
|
routingMark int
|
||||||
network int
|
network int
|
||||||
|
@ -38,6 +39,12 @@ func WithInterface(name string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithFallbackBind(fallback bool) Option {
|
||||||
|
return func(opt *option) {
|
||||||
|
opt.fallbackBind = fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithAddrReuse(reuse bool) Option {
|
func WithAddrReuse(reuse bool) Option {
|
||||||
return func(opt *option) {
|
return func(opt *option) {
|
||||||
opt.addrReuse = reuse
|
opt.addrReuse = reuse
|
||||||
|
|
|
@ -280,7 +280,7 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
|
||||||
DstPort: uint16(uintPort),
|
DstPort: uint16(uintPort),
|
||||||
}
|
}
|
||||||
if proxyAdapter == nil {
|
if proxyAdapter == nil {
|
||||||
return dialer.NewDialer(opts...).ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !proxyAdapter.SupportUDP() {
|
if !proxyAdapter.SupportUDP() {
|
||||||
|
|
Loading…
Reference in a new issue