Refactor: tun config
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -36,6 +35,7 @@ var (
|
||||
bindAddress = "*"
|
||||
lastTunConf *config.Tun
|
||||
lastTunAddressPrefix *netip.Prefix
|
||||
tunAddressPrefix *netip.Prefix
|
||||
|
||||
socksListener *socks.Listener
|
||||
socksUDPListener *socks.UDPListener
|
||||
@@ -70,11 +70,24 @@ type Ports struct {
|
||||
|
||||
func GetTunConf() config.Tun {
|
||||
if lastTunConf == nil {
|
||||
addrPort := C.DNSAddrPort{
|
||||
AddrPort: netip.MustParseAddrPort("0.0.0.0:53"),
|
||||
}
|
||||
return config.Tun{
|
||||
Enable: false,
|
||||
Stack: C.TunGvisor,
|
||||
DNSHijack: []netip.AddrPort{netip.MustParseAddrPort("0.0.0.0:53")},
|
||||
AutoRoute: true,
|
||||
Enable: false,
|
||||
Stack: C.TunGvisor,
|
||||
DNSHijack: []C.DNSUrl{ // default hijack all dns query
|
||||
{
|
||||
Network: "udp",
|
||||
AddrPort: addrPort,
|
||||
},
|
||||
{
|
||||
Network: "tcp",
|
||||
AddrPort: addrPort,
|
||||
},
|
||||
},
|
||||
AutoRoute: true,
|
||||
AutoDetectInterface: false,
|
||||
}
|
||||
}
|
||||
return *lastTunConf
|
||||
@@ -96,6 +109,10 @@ func SetBindAddress(host string) {
|
||||
bindAddress = host
|
||||
}
|
||||
|
||||
func SetTunAddressPrefix(tunAddress *netip.Prefix) {
|
||||
tunAddressPrefix = tunAddress
|
||||
}
|
||||
|
||||
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
|
||||
httpMux.Lock()
|
||||
defer httpMux.Unlock()
|
||||
@@ -335,7 +352,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
|
||||
log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address())
|
||||
}
|
||||
|
||||
func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
|
||||
func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
|
||||
tunMux.Lock()
|
||||
defer tunMux.Unlock()
|
||||
|
||||
@@ -350,6 +367,8 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan
|
||||
tunAddressPrefix = lastTunAddressPrefix
|
||||
}
|
||||
|
||||
tunConf.DNSHijack = C.RemoveDuplicateDNSUrl(tunConf.DNSHijack)
|
||||
|
||||
if tunStackListener != nil {
|
||||
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
|
||||
return
|
||||
@@ -519,14 +538,6 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
|
||||
return true
|
||||
}
|
||||
|
||||
sort.Slice(lastTunConf.DNSHijack, func(i, j int) bool {
|
||||
return lastTunConf.DNSHijack[i].Addr().Less(lastTunConf.DNSHijack[j].Addr())
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.DNSHijack, func(i, j int) bool {
|
||||
return tunConf.DNSHijack[i].Addr().Less(tunConf.DNSHijack[j].Addr())
|
||||
})
|
||||
|
||||
for i, dns := range tunConf.DNSHijack {
|
||||
if dns != lastTunConf.DNSHijack[i] {
|
||||
return true
|
||||
@@ -536,7 +547,8 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
|
||||
if lastTunConf.Enable != tunConf.Enable ||
|
||||
lastTunConf.Device != tunConf.Device ||
|
||||
lastTunConf.Stack != tunConf.Stack ||
|
||||
lastTunConf.AutoRoute != tunConf.AutoRoute {
|
||||
lastTunConf.AutoRoute != tunConf.AutoRoute ||
|
||||
lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const DefaultDnsReadTimeout = time.Second * 10
|
||||
|
||||
func ShouldHijackDns(dnsAdds []netip.AddrPort, targetAddr netip.AddrPort) bool {
|
||||
for _, addrPort := range dnsAdds {
|
||||
if addrPort == targetAddr || (addrPort.Addr().IsUnspecified() && targetAddr.Port() == 53) {
|
||||
func ShouldHijackDns(dnsHijack []C.DNSUrl, targetAddr netip.AddrPort, network string) bool {
|
||||
for _, dns := range dnsHijack {
|
||||
if dns.Network == network && (dns.AddrPort.AddrPort == targetAddr || (dns.AddrPort.Addr().IsUnspecified() && dns.AddrPort.Port() == targetAddr.Port())) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package commons
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
@@ -12,7 +13,10 @@ import (
|
||||
var (
|
||||
defaultRoutes = []string{"1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"}
|
||||
|
||||
defaultInterfaceMonitorDuration = 20 * time.Second
|
||||
monitorDuration = 10 * time.Second
|
||||
monitorStarted = false
|
||||
monitorStop = make(chan struct{}, 2)
|
||||
monitorMux sync.Mutex
|
||||
)
|
||||
|
||||
func ipv4MaskString(bits int) string {
|
||||
@@ -24,26 +28,52 @@ func ipv4MaskString(bits int) string {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
|
||||
}
|
||||
|
||||
func defaultInterfaceChangeMonitor() {
|
||||
t := time.NewTicker(defaultInterfaceMonitorDuration)
|
||||
func StartDefaultInterfaceChangeMonitor() {
|
||||
monitorMux.Lock()
|
||||
if monitorStarted {
|
||||
monitorMux.Unlock()
|
||||
return
|
||||
}
|
||||
monitorStarted = true
|
||||
monitorMux.Unlock()
|
||||
|
||||
select {
|
||||
case <-monitorStop:
|
||||
default:
|
||||
}
|
||||
|
||||
t := time.NewTicker(monitorDuration)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
<-t.C
|
||||
select {
|
||||
case <-t.C:
|
||||
interfaceName, err := GetAutoDetectInterface()
|
||||
if err != nil {
|
||||
log.Warnln("[TUN] default interface monitor err: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
interfaceName, err := GetAutoDetectInterface()
|
||||
if err != nil {
|
||||
log.Warnln("[TUN] default interface monitor exited, cause: %v", err)
|
||||
old := dialer.DefaultInterface.Load()
|
||||
if interfaceName == old {
|
||||
continue
|
||||
}
|
||||
|
||||
dialer.DefaultInterface.Store(interfaceName)
|
||||
|
||||
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
|
||||
case <-monitorStop:
|
||||
break
|
||||
}
|
||||
|
||||
old := dialer.DefaultInterface.Load()
|
||||
if interfaceName == old {
|
||||
continue
|
||||
}
|
||||
|
||||
dialer.DefaultInterface.Store(interfaceName)
|
||||
|
||||
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
|
||||
}
|
||||
}
|
||||
|
||||
func StopDefaultInterfaceChangeMonitor() {
|
||||
monitorMux.Lock()
|
||||
defer monitorMux.Unlock()
|
||||
|
||||
if monitorStarted {
|
||||
monitorStop <- struct{}{}
|
||||
monitorStarted = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,23 @@ package commons
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/common/cmd"
|
||||
"github.com/Dreamacro/clash/listener/tun/device"
|
||||
)
|
||||
|
||||
func GetAutoDetectInterface() (string, error) {
|
||||
return cmd.ExecCmd("/bin/bash -c /sbin/route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n")
|
||||
rs, err := cmd.ExecCmd("/bin/bash -c /sbin/route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if rs == "" || strings.HasSuffix(rs, "\n") {
|
||||
return "", fmt.Errorf("invalid interface name: %s", rs)
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
|
||||
@@ -54,8 +64,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
|
||||
}
|
||||
}
|
||||
|
||||
go defaultInterfaceChangeMonitor()
|
||||
|
||||
return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
|
||||
}
|
||||
}
|
||||
|
||||
go defaultInterfaceChangeMonitor()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -205,8 +205,6 @@ startOver:
|
||||
|
||||
wintunInterfaceName = dev.Name()
|
||||
|
||||
go defaultInterfaceChangeMonitor()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ var _ adapter.Handler = (*gvHandler)(nil)
|
||||
|
||||
type gvHandler struct {
|
||||
gateway netip.Addr
|
||||
dnsHijack []netip.AddrPort
|
||||
dnsHijack []C.DNSUrl
|
||||
|
||||
tcpIn chan<- C.ConnContext
|
||||
udpIn chan<- *inbound.PacketAdapter
|
||||
@@ -37,7 +37,7 @@ func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
|
||||
|
||||
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
||||
|
||||
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
|
||||
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "tcp") {
|
||||
go func() {
|
||||
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
||||
|
||||
@@ -111,7 +111,7 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
|
||||
|
||||
payload := buf[:n]
|
||||
|
||||
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
|
||||
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "udp") {
|
||||
go func() {
|
||||
defer func() {
|
||||
_ = pool.Put(buf)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/listener/tun/device"
|
||||
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
@@ -25,6 +26,8 @@ type gvStack struct {
|
||||
}
|
||||
|
||||
func (s *gvStack) Close() error {
|
||||
commons.StopDefaultInterfaceChangeMonitor()
|
||||
|
||||
var err error
|
||||
if s.device != nil {
|
||||
err = s.device.Close()
|
||||
@@ -37,7 +40,7 @@ func (s *gvStack) Close() error {
|
||||
}
|
||||
|
||||
// New allocates a new *gvStack with given options.
|
||||
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter, opts ...option.Option) (ipstack.Stack, error) {
|
||||
func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter, opts ...option.Option) (ipstack.Stack, error) {
|
||||
s := &gvStack{
|
||||
Stack: stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
|
||||
@@ -34,6 +34,8 @@ type sysStack struct {
|
||||
}
|
||||
|
||||
func (s *sysStack) Close() error {
|
||||
D.StopDefaultInterfaceChangeMonitor()
|
||||
|
||||
defer func() {
|
||||
if s.device != nil {
|
||||
_ = s.device.Close()
|
||||
@@ -49,7 +51,7 @@ func (s *sysStack) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||
func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||
var (
|
||||
gateway = tunAddress.Masked().Addr().Next()
|
||||
portal = gateway.Next()
|
||||
@@ -91,7 +93,7 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||
continue
|
||||
}
|
||||
|
||||
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
||||
if D.ShouldHijackDns(dnsAddr, rAddrPort, "tcp") {
|
||||
go func() {
|
||||
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
||||
|
||||
@@ -175,7 +177,7 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||
continue
|
||||
}
|
||||
|
||||
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
||||
if D.ShouldHijackDns(dnsAddr, rAddrPort, "udp") {
|
||||
go func() {
|
||||
msg, err := D.RelayDnsPacket(raw)
|
||||
if err != nil {
|
||||
|
||||
@@ -38,6 +38,12 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
|
||||
err error
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if err != nil && tunDevice != nil {
|
||||
_ = tunDevice.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if devName == "" {
|
||||
devName = generateDeviceName()
|
||||
}
|
||||
@@ -63,26 +69,22 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
|
||||
case C.TunGvisor:
|
||||
err = tunDevice.UseEndpoint()
|
||||
if err != nil {
|
||||
_ = tunDevice.Close()
|
||||
return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
|
||||
}
|
||||
|
||||
tunStack, err = gvisor.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn, option.WithDefault())
|
||||
|
||||
if err != nil {
|
||||
_ = tunDevice.Close()
|
||||
return nil, fmt.Errorf("can't New gvisor stack: %w", err)
|
||||
}
|
||||
case C.TunSystem:
|
||||
err = tunDevice.UseIOBased()
|
||||
if err != nil {
|
||||
_ = tunDevice.Close()
|
||||
return nil, fmt.Errorf("can't New system stack: %w", err)
|
||||
}
|
||||
|
||||
tunStack, err = system.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn)
|
||||
if err != nil {
|
||||
_ = tunDevice.Close()
|
||||
return nil, fmt.Errorf("can't New system stack: %w", err)
|
||||
}
|
||||
default:
|
||||
@@ -92,10 +94,13 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
|
||||
// setting address and routing
|
||||
err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute)
|
||||
if err != nil {
|
||||
_ = tunDevice.Close()
|
||||
return nil, fmt.Errorf("setting interface address and routing failed: %w", err)
|
||||
}
|
||||
|
||||
if tunConf.AutoDetectInterface {
|
||||
go commons.StartDefaultInterfaceChangeMonitor()
|
||||
}
|
||||
|
||||
setAtLatest(stackType, devName)
|
||||
|
||||
log.Infoln("TUN stack listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", tunDevice.Name(), tunAddress.Masked().Addr().Next().String(), mtu, autoRoute, stackType)
|
||||
|
||||
Reference in New Issue
Block a user