github.com/yaling888/clash@v1.53.0/listener/tun/tun_adapter.go (about)

     1  package tun
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/netip"
     7  	"net/url"
     8  	"runtime"
     9  	"strings"
    10  
    11  	"github.com/phuslu/log"
    12  
    13  	"github.com/yaling888/clash/adapter/inbound"
    14  	"github.com/yaling888/clash/common/cmd"
    15  	"github.com/yaling888/clash/component/dialer"
    16  	"github.com/yaling888/clash/component/resolver"
    17  	C "github.com/yaling888/clash/constant"
    18  	"github.com/yaling888/clash/listener/tun/device"
    19  	"github.com/yaling888/clash/listener/tun/device/tun"
    20  	"github.com/yaling888/clash/listener/tun/ipstack"
    21  	"github.com/yaling888/clash/listener/tun/ipstack/commons"
    22  	"github.com/yaling888/clash/listener/tun/ipstack/gvisor"
    23  	"github.com/yaling888/clash/listener/tun/ipstack/system"
    24  )
    25  
    26  // New TunAdapter
    27  func New(
    28  	tunConf *C.Tun,
    29  	tcpIn chan<- C.ConnContext,
    30  	udpIn chan<- *inbound.PacketAdapter,
    31  	tunChangeCallback C.TUNChangeCallback,
    32  ) (ipstack.Stack, error) {
    33  	var (
    34  		tunAddress = netip.Prefix{}
    35  		devName    = tunConf.Device
    36  		stackType  = tunConf.Stack
    37  		autoRoute  = tunConf.AutoRoute
    38  		mtu        = 1<<16 - 1
    39  
    40  		tunDevice device.Device
    41  		tunStack  ipstack.Stack
    42  
    43  		err error
    44  	)
    45  
    46  	defer func() {
    47  		if err != nil {
    48  			if tunStack != nil {
    49  				_ = tunStack.Close()
    50  			} else if tunDevice != nil {
    51  				_ = tunDevice.Close()
    52  			}
    53  		}
    54  	}()
    55  
    56  	defaultInterface := dialer.DefaultInterface.Load()
    57  	if tunConf.AutoDetectInterface {
    58  		commons.SetTunChangeCallback(tunChangeCallback)
    59  		commons.StartDefaultInterfaceChangeMonitor()
    60  		if defaultInterface == "" {
    61  			commons.SetTunStatus(C.TunPaused)
    62  			return nil, nil
    63  		}
    64  	} else if defaultInterface == "" {
    65  		return nil, errors.New(
    66  			"default interface not found, please assign value to `interface-name` or enable `auto-detect-interface`",
    67  		)
    68  	}
    69  
    70  	if devName == "" {
    71  		devName = generateDeviceName()
    72  	}
    73  
    74  	if tunConf.TunAddressPrefix != nil {
    75  		tunAddress = *tunConf.TunAddressPrefix
    76  	}
    77  
    78  	if !tunAddress.IsValid() || !tunAddress.Addr().Is4() {
    79  		tunAddress = netip.MustParsePrefix("198.18.0.1/16")
    80  	}
    81  
    82  	// open tun device
    83  	tunDevice, err = parseDevice(devName, uint32(mtu))
    84  	if err != nil {
    85  		return nil, fmt.Errorf("can't open tun: %w", err)
    86  	}
    87  
    88  	devName = tunDevice.Name()
    89  
    90  	// new ip stack
    91  	switch stackType {
    92  	case C.TunGvisor:
    93  		err = tunDevice.UseEndpoint()
    94  		if err != nil {
    95  			return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
    96  		}
    97  
    98  		tunStack, err = gvisor.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn)
    99  		if err != nil {
   100  			return nil, fmt.Errorf("can't New gvisor stack: %w", err)
   101  		}
   102  	case C.TunSystem:
   103  		err = tunDevice.UseIOBased()
   104  		if err != nil {
   105  			return nil, fmt.Errorf("can't New system stack: %w", err)
   106  		}
   107  
   108  		tunStack, err = system.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn)
   109  		if err != nil {
   110  			return nil, fmt.Errorf("can't New system stack: %w", err)
   111  		}
   112  	default:
   113  		return nil, errors.New("unknown ip stack")
   114  	}
   115  
   116  	// setting address and routing
   117  	err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute)
   118  	if err != nil {
   119  		return nil, fmt.Errorf("setting interface address and routing failed: %w", err)
   120  	}
   121  
   122  	if autoRoute {
   123  		resolver.DisableIPv6 = true
   124  	}
   125  
   126  	tunConf.Device = devName
   127  	setAtLatest(stackType, devName)
   128  
   129  	log.Info().
   130  		Str("iface", devName).
   131  		NetIPAddr("gateway", tunAddress.Masked().Addr().Next()).
   132  		Uint32("mtu", tunDevice.MTU()).
   133  		Int("batchSize", tunDevice.BatchSize()).
   134  		Bool("autoRoute", autoRoute).
   135  		Bool("autoDetectInterface", tunConf.AutoDetectInterface).
   136  		Str("ipStack", stackType.String()).
   137  		Msg("[Inbound] tun listening")
   138  	return tunStack, nil
   139  }
   140  
   141  func generateDeviceName() string {
   142  	switch runtime.GOOS {
   143  	case "darwin":
   144  		return tun.Driver + "://utun"
   145  	case "windows":
   146  		return tun.Driver + "://Clash"
   147  	default:
   148  		return tun.Driver + "://clash0"
   149  	}
   150  }
   151  
   152  func parseDevice(s string, mtu uint32) (device.Device, error) {
   153  	if !strings.Contains(s, "://") {
   154  		s = fmt.Sprintf("%s://%s", tun.Driver /* default driver */, s)
   155  	}
   156  
   157  	u, err := url.Parse(s)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	name := u.Host
   163  	return tun.Open(name, mtu)
   164  }
   165  
   166  func setAtLatest(stackType C.TUNStack, devName string) {
   167  	switch runtime.GOOS {
   168  	case "darwin":
   169  		// _, _ = cmd.ExecCmd("/usr/sbin/sysctl -w net.inet.ip.forwarding=1")
   170  		// _, _ = cmd.ExecCmd("/usr/sbin/sysctl -w net.inet6.ip6.forwarding=1")
   171  		_, _ = cmd.ExecCmd("/bin/launchctl limit maxfiles 10240 unlimited")
   172  	case "windows":
   173  		if stackType != C.TunSystem {
   174  			return
   175  		}
   176  		_, _ = cmd.ExecCmd("ipconfig /renew")
   177  	case "linux":
   178  		_, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1")
   179  		_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding=1")
   180  		_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_local=1")
   181  		_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_redirects=1")
   182  		_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.rp_filter=0")
   183  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.forwarding=1", devName))
   184  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_local=1", devName))
   185  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_redirects=1", devName))
   186  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.rp_filter=0", devName))
   187  		//_, _ = cmd.ExecCmd("iptables -t filter -P FORWARD ACCEPT")
   188  	}
   189  }