Refactor: tun config

This commit is contained in:
yaling888
2022-05-16 01:21:56 +08:00
parent e92bea8401
commit 8b3e42bf19
19 changed files with 375 additions and 163 deletions

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -42,8 +42,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
}
}
go defaultInterfaceChangeMonitor()
return nil
}

View File

@@ -205,8 +205,6 @@ startOver:
wintunInterfaceName = dev.Name()
go defaultInterfaceChangeMonitor()
return nil
}

View File

@@ -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)

View File

@@ -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{

View File

@@ -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 {

View File

@@ -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)