github.com/MerlinKodo/sing-tun@v0.1.15/tun_linux.go (about)

     1  package tun
     2  
     3  import (
     4  	"math/rand"
     5  	"net"
     6  	"net/netip"
     7  	"os"
     8  	"os/exec"
     9  	"runtime"
    10  	"syscall"
    11  	"unsafe"
    12  
    13  	"github.com/sagernet/netlink"
    14  	"github.com/sagernet/sing/common"
    15  	"github.com/sagernet/sing/common/bufio"
    16  	E "github.com/sagernet/sing/common/exceptions"
    17  	N "github.com/sagernet/sing/common/network"
    18  	"github.com/sagernet/sing/common/rw"
    19  	"github.com/sagernet/sing/common/shell"
    20  	"github.com/sagernet/sing/common/x/list"
    21  
    22  	"golang.org/x/sys/unix"
    23  )
    24  
    25  type NativeTun struct {
    26  	tunFd             int
    27  	tunFile           *os.File
    28  	interfaceCallback *list.Element[DefaultInterfaceUpdateCallback]
    29  	options           Options
    30  	ruleIndex6        []int
    31  }
    32  
    33  func New(options Options) (Tun, error) {
    34  	if options.FileDescriptor == 0 {
    35  		tunFd, err := open(options.Name)
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  		tunLink, err := netlink.LinkByName(options.Name)
    40  		if err != nil {
    41  			return nil, E.Errors(err, unix.Close(tunFd))
    42  		}
    43  		nativeTun := &NativeTun{
    44  			tunFd:   tunFd,
    45  			tunFile: os.NewFile(uintptr(tunFd), "tun"),
    46  			options: options,
    47  		}
    48  		runtime.SetFinalizer(nativeTun.tunFile, nil)
    49  		err = nativeTun.configure(tunLink)
    50  		if err != nil {
    51  			return nil, E.Errors(err, unix.Close(tunFd))
    52  		}
    53  		return nativeTun, nil
    54  	} else {
    55  		nativeTun := &NativeTun{
    56  			tunFd:   options.FileDescriptor,
    57  			tunFile: os.NewFile(uintptr(options.FileDescriptor), "tun"),
    58  			options: options,
    59  		}
    60  		runtime.SetFinalizer(nativeTun.tunFile, nil)
    61  		return nativeTun, nil
    62  	}
    63  }
    64  
    65  func (t *NativeTun) Read(p []byte) (n int, err error) {
    66  	return t.tunFile.Read(p)
    67  }
    68  
    69  func (t *NativeTun) Write(p []byte) (n int, err error) {
    70  	return t.tunFile.Write(p)
    71  }
    72  
    73  func (t *NativeTun) CreateVectorisedWriter() N.VectorisedWriter {
    74  	return bufio.NewVectorisedWriter(t.tunFile)
    75  }
    76  
    77  var controlPath string
    78  
    79  func init() {
    80  	const defaultTunPath = "/dev/net/tun"
    81  	const androidTunPath = "/dev/tun"
    82  	if rw.FileExists(androidTunPath) {
    83  		controlPath = androidTunPath
    84  	} else {
    85  		controlPath = defaultTunPath
    86  	}
    87  }
    88  
    89  func open(name string) (int, error) {
    90  	fd, err := unix.Open(controlPath, unix.O_RDWR, 0)
    91  	if err != nil {
    92  		return -1, err
    93  	}
    94  
    95  	var ifr struct {
    96  		name  [16]byte
    97  		flags uint16
    98  		_     [22]byte
    99  	}
   100  
   101  	copy(ifr.name[:], name)
   102  	ifr.flags = unix.IFF_TUN | unix.IFF_NO_PI
   103  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.TUNSETIFF, uintptr(unsafe.Pointer(&ifr)))
   104  	if errno != 0 {
   105  		unix.Close(fd)
   106  		return -1, errno
   107  	}
   108  
   109  	if err = unix.SetNonblock(fd, true); err != nil {
   110  		unix.Close(fd)
   111  		return -1, err
   112  	}
   113  
   114  	return fd, nil
   115  }
   116  
   117  func (t *NativeTun) configure(tunLink netlink.Link) error {
   118  	err := netlink.LinkSetMTU(tunLink, int(t.options.MTU))
   119  	if err == unix.EPERM {
   120  		// unprivileged
   121  		return nil
   122  	} else if err != nil {
   123  		return err
   124  	}
   125  
   126  	if len(t.options.Inet4Address) > 0 {
   127  		for _, address := range t.options.Inet4Address {
   128  			addr4, _ := netlink.ParseAddr(address.String())
   129  			err = netlink.AddrAdd(tunLink, addr4)
   130  			if err != nil {
   131  				return err
   132  			}
   133  		}
   134  	}
   135  	if len(t.options.Inet6Address) > 0 {
   136  		for _, address := range t.options.Inet6Address {
   137  			addr6, _ := netlink.ParseAddr(address.String())
   138  			err = netlink.AddrAdd(tunLink, addr6)
   139  			if err != nil {
   140  				return err
   141  			}
   142  		}
   143  	}
   144  
   145  	err = netlink.LinkSetUp(tunLink)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	if t.options.TableIndex == 0 {
   151  		for {
   152  			t.options.TableIndex = int(rand.Uint32())
   153  			routeList, fErr := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{Table: t.options.TableIndex}, netlink.RT_FILTER_TABLE)
   154  			if len(routeList) == 0 || fErr != nil {
   155  				break
   156  			}
   157  		}
   158  	}
   159  
   160  	err = t.setRoute(tunLink)
   161  	if err != nil {
   162  		_ = t.unsetRoute0(tunLink)
   163  		return err
   164  	}
   165  
   166  	err = t.unsetRules()
   167  	if err != nil {
   168  		return E.Cause(err, "cleanup rules")
   169  	}
   170  	err = t.setRules()
   171  	if err != nil {
   172  		_ = t.unsetRules()
   173  		return err
   174  	}
   175  
   176  	t.setSearchDomainForSystemdResolved()
   177  
   178  	if t.options.AutoRoute && runtime.GOOS == "android" {
   179  		t.interfaceCallback = t.options.InterfaceMonitor.RegisterCallback(t.routeUpdate)
   180  	}
   181  	return nil
   182  }
   183  
   184  func (t *NativeTun) Close() error {
   185  	if t.interfaceCallback != nil {
   186  		t.options.InterfaceMonitor.UnregisterCallback(t.interfaceCallback)
   187  	}
   188  	return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
   189  }
   190  
   191  func prefixToIPNet(prefix netip.Prefix) *net.IPNet {
   192  	return &net.IPNet{
   193  		IP:   prefix.Addr().AsSlice(),
   194  		Mask: net.CIDRMask(prefix.Bits(), prefix.Addr().BitLen()),
   195  	}
   196  }
   197  
   198  func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
   199  	var routes []netlink.Route
   200  	for _, address := range t.options.Inet6Address {
   201  		routes = append(routes, netlink.Route{
   202  			Dst:       prefixToIPNet(address),
   203  			LinkIndex: tunLink.Attrs().Index,
   204  			Table:     t.options.TableIndex,
   205  		})
   206  	}
   207  	if t.options.AutoRoute {
   208  		routeRanges, err := t.options.BuildAutoRouteRanges()
   209  		if err != nil {
   210  			return nil, err
   211  		}
   212  		for _, routeRange := range routeRanges {
   213  			routes = append(routes, netlink.Route{
   214  				Dst:       prefixToIPNet(routeRange),
   215  				LinkIndex: tunLink.Attrs().Index,
   216  				Table:     t.options.TableIndex,
   217  			})
   218  		}
   219  	}
   220  	return routes, nil
   221  }
   222  
   223  const (
   224  	ruleStart = 9000
   225  	ruleEnd   = ruleStart + 10
   226  )
   227  
   228  func (t *NativeTun) nextIndex6() int {
   229  	ruleList, err := netlink.RuleList(netlink.FAMILY_V6)
   230  	if err != nil {
   231  		return -1
   232  	}
   233  	var minIndex int
   234  	for _, rule := range ruleList {
   235  		if rule.Priority > 0 && (minIndex == 0 || rule.Priority < minIndex) {
   236  			minIndex = rule.Priority
   237  		}
   238  	}
   239  	minIndex--
   240  	t.ruleIndex6 = append(t.ruleIndex6, minIndex)
   241  	return minIndex
   242  }
   243  
   244  func (t *NativeTun) rules() []*netlink.Rule {
   245  	if !t.options.AutoRoute {
   246  		if len(t.options.Inet6Address) > 0 {
   247  			it := netlink.NewRule()
   248  			it.Priority = t.nextIndex6()
   249  			it.Table = t.options.TableIndex
   250  			it.Family = unix.AF_INET6
   251  			it.OifName = t.options.Name
   252  			return []*netlink.Rule{it}
   253  		}
   254  		return nil
   255  	}
   256  
   257  	var p4, p6 bool
   258  	var pRule int
   259  	if len(t.options.Inet4Address) > 0 {
   260  		p4 = true
   261  		pRule += 1
   262  	}
   263  	if len(t.options.Inet6Address) > 0 {
   264  		p6 = true
   265  		pRule += 1
   266  	}
   267  	if pRule == 0 {
   268  		return []*netlink.Rule{}
   269  	}
   270  
   271  	var rules []*netlink.Rule
   272  	var it *netlink.Rule
   273  
   274  	excludeRanges := t.options.ExcludedRanges()
   275  	priority := ruleStart
   276  	priority6 := priority
   277  	nopPriority := ruleEnd
   278  
   279  	for _, excludeRange := range excludeRanges {
   280  		if p4 {
   281  			it = netlink.NewRule()
   282  			it.Priority = priority
   283  			it.UIDRange = netlink.NewRuleUIDRange(excludeRange.Start, excludeRange.End)
   284  			it.Goto = nopPriority
   285  			it.Family = unix.AF_INET
   286  			rules = append(rules, it)
   287  		}
   288  		if p6 {
   289  			it = netlink.NewRule()
   290  			it.Priority = priority6
   291  			it.UIDRange = netlink.NewRuleUIDRange(excludeRange.Start, excludeRange.End)
   292  			it.Goto = nopPriority
   293  			it.Family = unix.AF_INET6
   294  			rules = append(rules, it)
   295  		}
   296  	}
   297  	if len(excludeRanges) > 0 {
   298  		if p4 {
   299  			priority++
   300  		}
   301  		if p6 {
   302  			priority6++
   303  		}
   304  	}
   305  	if len(t.options.IncludeInterface) > 0 {
   306  		matchPriority := priority + 2*len(t.options.IncludeInterface) + 1
   307  		for _, includeInterface := range t.options.IncludeInterface {
   308  			if p4 {
   309  				it = netlink.NewRule()
   310  				it.Priority = priority
   311  				it.IifName = includeInterface
   312  				it.Goto = matchPriority
   313  				it.Family = unix.AF_INET
   314  				rules = append(rules, it)
   315  				priority++
   316  
   317  				it = netlink.NewRule()
   318  				it.Priority = priority
   319  				it.OifName = includeInterface
   320  				it.Goto = matchPriority
   321  				it.Family = unix.AF_INET
   322  				rules = append(rules, it)
   323  				priority++
   324  			}
   325  			if p6 {
   326  				it = netlink.NewRule()
   327  				it.Priority = priority6
   328  				it.IifName = includeInterface
   329  				it.Goto = matchPriority
   330  				it.Family = unix.AF_INET6
   331  				rules = append(rules, it)
   332  				priority6++
   333  
   334  				it = netlink.NewRule()
   335  				it.Priority = priority6
   336  				it.OifName = includeInterface
   337  				it.Goto = matchPriority
   338  				it.Family = unix.AF_INET6
   339  				rules = append(rules, it)
   340  				priority6++
   341  			}
   342  		}
   343  		if p4 {
   344  			it = netlink.NewRule()
   345  			it.Priority = priority
   346  			it.Family = unix.AF_INET
   347  			it.Goto = nopPriority
   348  			rules = append(rules, it)
   349  			priority++
   350  
   351  			it = netlink.NewRule()
   352  			it.Priority = matchPriority
   353  			it.Family = unix.AF_INET
   354  			rules = append(rules, it)
   355  			priority++
   356  		}
   357  		if p6 {
   358  			it = netlink.NewRule()
   359  			it.Priority = priority6
   360  			it.Family = unix.AF_INET6
   361  			it.Goto = nopPriority
   362  			rules = append(rules, it)
   363  			priority6++
   364  
   365  			it = netlink.NewRule()
   366  			it.Priority = matchPriority
   367  			it.Family = unix.AF_INET6
   368  			rules = append(rules, it)
   369  			priority6++
   370  		}
   371  	} else if len(t.options.ExcludeInterface) > 0 {
   372  		for _, excludeInterface := range t.options.ExcludeInterface {
   373  			if p4 {
   374  				it = netlink.NewRule()
   375  				it.Priority = priority
   376  				it.IifName = excludeInterface
   377  				it.Goto = nopPriority
   378  				it.Family = unix.AF_INET
   379  				rules = append(rules, it)
   380  				priority++
   381  
   382  				it = netlink.NewRule()
   383  				it.Priority = priority
   384  				it.OifName = excludeInterface
   385  				it.Goto = nopPriority
   386  				it.Family = unix.AF_INET
   387  				rules = append(rules, it)
   388  				priority++
   389  			}
   390  			if p6 {
   391  				it = netlink.NewRule()
   392  				it.Priority = priority6
   393  				it.IifName = excludeInterface
   394  				it.Goto = nopPriority
   395  				it.Family = unix.AF_INET6
   396  				rules = append(rules, it)
   397  				priority6++
   398  
   399  				it = netlink.NewRule()
   400  				it.Priority = priority6
   401  				it.OifName = excludeInterface
   402  				it.Goto = nopPriority
   403  				it.Family = unix.AF_INET6
   404  				rules = append(rules, it)
   405  				priority6++
   406  			}
   407  		}
   408  	}
   409  
   410  	if runtime.GOOS == "android" && t.options.InterfaceMonitor.AndroidVPNEnabled() {
   411  		const protectedFromVPN = 0x20000
   412  		if p4 || t.options.StrictRoute {
   413  			it = netlink.NewRule()
   414  			if t.options.InterfaceMonitor.OverrideAndroidVPN() {
   415  				it.Mark = protectedFromVPN
   416  			}
   417  			it.Mask = protectedFromVPN
   418  			it.Priority = priority
   419  			it.Family = unix.AF_INET
   420  			it.Goto = nopPriority
   421  			rules = append(rules, it)
   422  			priority++
   423  		}
   424  		if p6 || t.options.StrictRoute {
   425  			it = netlink.NewRule()
   426  			if t.options.InterfaceMonitor.OverrideAndroidVPN() {
   427  				it.Mark = protectedFromVPN
   428  			}
   429  			it.Mask = protectedFromVPN
   430  			it.Family = unix.AF_INET6
   431  			it.Priority = priority6
   432  			it.Goto = nopPriority
   433  			rules = append(rules, it)
   434  			priority6++
   435  		}
   436  	}
   437  
   438  	if t.options.StrictRoute {
   439  		if !p4 {
   440  			it = netlink.NewRule()
   441  			it.Priority = priority
   442  			it.Family = unix.AF_INET
   443  			it.Type = unix.FR_ACT_UNREACHABLE
   444  			rules = append(rules, it)
   445  			priority++
   446  		}
   447  		if !p6 {
   448  			it = netlink.NewRule()
   449  			it.Priority = priority6
   450  			it.Family = unix.AF_INET6
   451  			it.Type = unix.FR_ACT_UNREACHABLE
   452  			rules = append(rules, it)
   453  			priority6++
   454  		}
   455  	}
   456  
   457  	if runtime.GOOS != "android" {
   458  		if p4 {
   459  			for _, address := range t.options.Inet4Address {
   460  				it = netlink.NewRule()
   461  				it.Priority = priority
   462  				it.Dst = address.Masked()
   463  				it.Table = t.options.TableIndex
   464  				it.Family = unix.AF_INET
   465  				rules = append(rules, it)
   466  			}
   467  			priority++
   468  		}
   469  		/*if p6 {
   470  			it = netlink.NewRule()
   471  			it.Priority = priority
   472  			it.Dst = t.options.Inet6Address.Masked()
   473  			it.Table = tunTableIndex
   474  			it.Family = unix.AF_INET6
   475  			rules = append(rules, it)
   476  		}*/
   477  		if p4 {
   478  			it = netlink.NewRule()
   479  			it.Priority = priority
   480  			it.IPProto = syscall.IPPROTO_ICMP
   481  			it.Goto = nopPriority
   482  			it.Family = unix.AF_INET
   483  			rules = append(rules, it)
   484  			priority++
   485  		}
   486  		if p6 {
   487  			it = netlink.NewRule()
   488  			it.Priority = priority6
   489  			it.IPProto = syscall.IPPROTO_ICMPV6
   490  			it.Goto = nopPriority
   491  			it.Family = unix.AF_INET6
   492  			rules = append(rules, it)
   493  			priority6++
   494  		}
   495  		if p4 {
   496  			it = netlink.NewRule()
   497  			it.Priority = priority
   498  			it.Invert = true
   499  			it.Dport = netlink.NewRulePortRange(53, 53)
   500  			it.Table = unix.RT_TABLE_MAIN
   501  			it.SuppressPrefixlen = 0
   502  			it.Family = unix.AF_INET
   503  			rules = append(rules, it)
   504  		}
   505  		if p6 {
   506  			it = netlink.NewRule()
   507  			it.Priority = priority6
   508  			it.Invert = true
   509  			it.Dport = netlink.NewRulePortRange(53, 53)
   510  			it.Table = unix.RT_TABLE_MAIN
   511  			it.SuppressPrefixlen = 0
   512  			it.Family = unix.AF_INET6
   513  			rules = append(rules, it)
   514  		}
   515  	}
   516  
   517  	if p4 {
   518  		if t.options.StrictRoute {
   519  			it = netlink.NewRule()
   520  			it.Priority = priority
   521  			it.Table = t.options.TableIndex
   522  			it.Family = unix.AF_INET
   523  			rules = append(rules, it)
   524  		} else {
   525  			if runtime.GOOS == "android" {
   526  				it = netlink.NewRule()
   527  				it.Priority = priority
   528  				it.IifName = t.options.Name
   529  				it.Table = 254 //main
   530  				it.Family = unix.AF_INET
   531  				it.SuppressPrefixlen = 0
   532  				rules = append(rules, it)
   533  			}
   534  			it = netlink.NewRule()
   535  			it.Priority = priority
   536  			it.Invert = true
   537  			it.IifName = "lo"
   538  			it.Table = t.options.TableIndex
   539  			it.Family = unix.AF_INET
   540  			rules = append(rules, it)
   541  
   542  			it = netlink.NewRule()
   543  			it.Priority = priority
   544  			it.IifName = "lo"
   545  			it.Src = netip.PrefixFrom(netip.IPv4Unspecified(), 32)
   546  			it.Table = t.options.TableIndex
   547  			it.Family = unix.AF_INET
   548  			rules = append(rules, it)
   549  
   550  			for _, address := range t.options.Inet4Address {
   551  				it = netlink.NewRule()
   552  				it.Priority = priority
   553  				it.IifName = "lo"
   554  				it.Src = address.Masked()
   555  				it.Table = t.options.TableIndex
   556  				it.Family = unix.AF_INET
   557  				rules = append(rules, it)
   558  			}
   559  		}
   560  		priority++
   561  	}
   562  	if p6 {
   563  		if !t.options.StrictRoute {
   564  			it = netlink.NewRule()
   565  			it.Priority = priority6
   566  			it.IifName = "lo"
   567  			it.Src = netip.PrefixFrom(netip.IPv6Unspecified(), 1)
   568  			it.Goto = nopPriority
   569  			it.Family = unix.AF_INET6
   570  			rules = append(rules, it)
   571  
   572  			it = netlink.NewRule()
   573  			it.Priority = priority6
   574  			it.IifName = "lo"
   575  			it.Src = netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 128}), 1)
   576  			it.Goto = nopPriority
   577  			it.Family = unix.AF_INET6
   578  			rules = append(rules, it)
   579  
   580  			priority6++
   581  		}
   582  
   583  		it = netlink.NewRule()
   584  		it.Priority = priority6
   585  		it.Table = t.options.TableIndex
   586  		it.Family = unix.AF_INET6
   587  		rules = append(rules, it)
   588  		priority6++
   589  	}
   590  	if p4 {
   591  		it = netlink.NewRule()
   592  		it.Priority = nopPriority
   593  		it.Family = unix.AF_INET
   594  		rules = append(rules, it)
   595  	}
   596  	if p6 {
   597  		it = netlink.NewRule()
   598  		it.Priority = nopPriority
   599  		it.Family = unix.AF_INET6
   600  		rules = append(rules, it)
   601  	}
   602  	return rules
   603  }
   604  
   605  func (t *NativeTun) setRoute(tunLink netlink.Link) error {
   606  	routes, err := t.routes(tunLink)
   607  	if err != nil {
   608  		return err
   609  	}
   610  	for i, route := range routes {
   611  		err := netlink.RouteAdd(&route)
   612  		if err != nil {
   613  			return E.Cause(err, "add route ", i)
   614  		}
   615  	}
   616  	return nil
   617  }
   618  
   619  func (t *NativeTun) setRules() error {
   620  	for i, rule := range t.rules() {
   621  		err := netlink.RuleAdd(rule)
   622  		if err != nil {
   623  			return E.Cause(err, "add rule ", i, "/", len(t.rules()))
   624  		}
   625  	}
   626  	return nil
   627  }
   628  
   629  func (t *NativeTun) unsetRoute() error {
   630  	if t.options.FileDescriptor > 0 {
   631  		return nil
   632  	}
   633  	tunLink, err := netlink.LinkByName(t.options.Name)
   634  	if err != nil {
   635  		return err
   636  	}
   637  	return t.unsetRoute0(tunLink)
   638  }
   639  
   640  func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
   641  	if routes, err := t.routes(tunLink); err == nil {
   642  		for _, route := range routes {
   643  			_ = netlink.RouteDel(&route)
   644  		}
   645  	}
   646  	return nil
   647  }
   648  
   649  func (t *NativeTun) unsetRules() error {
   650  	if t.options.FileDescriptor > 0 {
   651  		return nil
   652  	}
   653  	if len(t.ruleIndex6) > 0 {
   654  		for _, index := range t.ruleIndex6 {
   655  			ruleToDel := netlink.NewRule()
   656  			ruleToDel.Family = unix.AF_INET6
   657  			ruleToDel.Priority = index
   658  			err := netlink.RuleDel(ruleToDel)
   659  			if err != nil {
   660  				return E.Cause(err, "unset rule6 ", index)
   661  			}
   662  		}
   663  		t.ruleIndex6 = nil
   664  	}
   665  	if t.options.AutoRoute {
   666  		ruleList, err := netlink.RuleList(netlink.FAMILY_ALL)
   667  		if err != nil {
   668  			return err
   669  		}
   670  		for _, rule := range ruleList {
   671  			if rule.Priority >= ruleStart && rule.Priority <= ruleEnd {
   672  				ruleToDel := netlink.NewRule()
   673  				ruleToDel.Family = rule.Family
   674  				ruleToDel.Priority = rule.Priority
   675  				err = netlink.RuleDel(ruleToDel)
   676  				if err != nil {
   677  					return E.Cause(err, "unset rule ", rule.Priority, " for ", rule.Family)
   678  				}
   679  			}
   680  		}
   681  	}
   682  	return nil
   683  }
   684  
   685  func (t *NativeTun) resetRules() error {
   686  	t.unsetRules()
   687  	return t.setRules()
   688  }
   689  
   690  func (t *NativeTun) routeUpdate(event int) {
   691  	if event&EventAndroidVPNUpdate == 0 {
   692  		return
   693  	}
   694  	err := t.resetRules()
   695  	if err != nil {
   696  		if t.options.Logger != nil {
   697  			t.options.Logger.Error(E.Cause(err, "reset route"))
   698  		}
   699  	}
   700  }
   701  
   702  func (t *NativeTun) setSearchDomainForSystemdResolved() {
   703  	ctlPath, err := exec.LookPath("resolvectl")
   704  	if err != nil {
   705  		return
   706  	}
   707  	var dnsServer []netip.Addr
   708  	if len(t.options.Inet4Address) > 0 {
   709  		dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
   710  	}
   711  	if len(t.options.Inet6Address) > 0 {
   712  		dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
   713  	}
   714  	shell.Exec(ctlPath, "domain", t.options.Name, "~.").Start()
   715  	if t.options.AutoRoute {
   716  		shell.Exec(ctlPath, "default-route", t.options.Name, "true").Start()
   717  		shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Start()
   718  	}
   719  }