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

     1  package tun
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/netip"
     7  	"os"
     8  	"runtime"
     9  	"syscall"
    10  	"unsafe"
    11  
    12  	"github.com/sagernet/sing/common"
    13  	"github.com/sagernet/sing/common/buf"
    14  	"github.com/sagernet/sing/common/bufio"
    15  	E "github.com/sagernet/sing/common/exceptions"
    16  	N "github.com/sagernet/sing/common/network"
    17  	"github.com/sagernet/sing/common/shell"
    18  
    19  	"golang.org/x/net/route"
    20  	"golang.org/x/sys/unix"
    21  )
    22  
    23  const PacketOffset = 4
    24  
    25  type NativeTun struct {
    26  	tunFile      *os.File
    27  	tunWriter    N.VectorisedWriter
    28  	mtu          uint32
    29  	inet4Address [4]byte
    30  	inet6Address [16]byte
    31  }
    32  
    33  func New(options Options) (Tun, error) {
    34  	var tunFd int
    35  	if options.FileDescriptor == 0 {
    36  		ifIndex := -1
    37  		_, err := fmt.Sscanf(options.Name, "utun%d", &ifIndex)
    38  		if err != nil {
    39  			return nil, E.New("bad tun name: ", options.Name)
    40  		}
    41  
    42  		tunFd, err = unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  
    47  		err = configure(tunFd, ifIndex, options.Name, options)
    48  		if err != nil {
    49  			unix.Close(tunFd)
    50  			return nil, err
    51  		}
    52  	} else {
    53  		tunFd = options.FileDescriptor
    54  	}
    55  
    56  	nativeTun := &NativeTun{
    57  		tunFile: os.NewFile(uintptr(tunFd), "utun"),
    58  		mtu:     options.MTU,
    59  	}
    60  	if len(options.Inet4Address) > 0 {
    61  		nativeTun.inet4Address = options.Inet4Address[0].Addr().As4()
    62  	}
    63  	if len(options.Inet6Address) > 0 {
    64  		nativeTun.inet6Address = options.Inet6Address[0].Addr().As16()
    65  	}
    66  	var ok bool
    67  	nativeTun.tunWriter, ok = bufio.CreateVectorisedWriter(nativeTun.tunFile)
    68  	if !ok {
    69  		panic("create vectorised writer")
    70  	}
    71  	runtime.SetFinalizer(nativeTun.tunFile, nil)
    72  	return nativeTun, nil
    73  }
    74  
    75  func (t *NativeTun) Read(p []byte) (n int, err error) {
    76  	/*n, err = t.tunFile.Read(p)
    77  	if n < 4 {
    78  		return 0, err
    79  	}
    80  
    81  	copy(p[:], p[4:])
    82  	return n - 4, err*/
    83  	return t.tunFile.Read(p)
    84  }
    85  
    86  var (
    87  	packetHeader4 = [4]byte{0x00, 0x00, 0x00, unix.AF_INET}
    88  	packetHeader6 = [4]byte{0x00, 0x00, 0x00, unix.AF_INET6}
    89  )
    90  
    91  func (t *NativeTun) Write(p []byte) (n int, err error) {
    92  	var packetHeader []byte
    93  	if p[0]>>4 == 4 {
    94  		packetHeader = packetHeader4[:]
    95  	} else {
    96  		packetHeader = packetHeader6[:]
    97  	}
    98  	_, err = bufio.WriteVectorised(t.tunWriter, [][]byte{packetHeader, p})
    99  	if err == nil {
   100  		n = len(p)
   101  	}
   102  	return
   103  }
   104  
   105  func (t *NativeTun) CreateVectorisedWriter() N.VectorisedWriter {
   106  	return t
   107  }
   108  
   109  func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
   110  	var packetHeader []byte
   111  	if buffers[0].Byte(0)>>4 == 4 {
   112  		packetHeader = packetHeader4[:]
   113  	} else {
   114  		packetHeader = packetHeader6[:]
   115  	}
   116  	return t.tunWriter.WriteVectorised(append([]*buf.Buffer{buf.As(packetHeader)}, buffers...))
   117  }
   118  
   119  func (t *NativeTun) Close() error {
   120  	flushDNSCache()
   121  	return t.tunFile.Close()
   122  }
   123  
   124  const utunControlName = "com.apple.net.utun_control"
   125  
   126  const (
   127  	SIOCAIFADDR_IN6       = 2155899162 // netinet6/in6_var.h
   128  	IN6_IFF_NODAD         = 0x0020     // netinet6/in6_var.h
   129  	IN6_IFF_SECURED       = 0x0400     // netinet6/in6_var.h
   130  	ND6_INFINITE_LIFETIME = 0xFFFFFFFF // netinet6/nd6.h
   131  )
   132  
   133  type ifAliasReq struct {
   134  	Name    [unix.IFNAMSIZ]byte
   135  	Addr    unix.RawSockaddrInet4
   136  	Dstaddr unix.RawSockaddrInet4
   137  	Mask    unix.RawSockaddrInet4
   138  }
   139  
   140  type ifAliasReq6 struct {
   141  	Name     [16]byte
   142  	Addr     unix.RawSockaddrInet6
   143  	Dstaddr  unix.RawSockaddrInet6
   144  	Mask     unix.RawSockaddrInet6
   145  	Flags    uint32
   146  	Lifetime addrLifetime6
   147  }
   148  
   149  type addrLifetime6 struct {
   150  	Expire    float64
   151  	Preferred float64
   152  	Vltime    uint32
   153  	Pltime    uint32
   154  }
   155  
   156  func configure(tunFd int, ifIndex int, name string, options Options) error {
   157  	ctlInfo := &unix.CtlInfo{}
   158  	copy(ctlInfo.Name[:], utunControlName)
   159  	err := unix.IoctlCtlInfo(tunFd, ctlInfo)
   160  	if err != nil {
   161  		return os.NewSyscallError("IoctlCtlInfo", err)
   162  	}
   163  
   164  	err = unix.Connect(tunFd, &unix.SockaddrCtl{
   165  		ID:   ctlInfo.Id,
   166  		Unit: uint32(ifIndex) + 1,
   167  	})
   168  	if err != nil {
   169  		return os.NewSyscallError("Connect", err)
   170  	}
   171  
   172  	err = unix.SetNonblock(tunFd, true)
   173  	if err != nil {
   174  		return os.NewSyscallError("SetNonblock", err)
   175  	}
   176  
   177  	err = useSocket(unix.AF_INET, unix.SOCK_DGRAM, 0, func(socketFd int) error {
   178  		var ifr unix.IfreqMTU
   179  		copy(ifr.Name[:], name)
   180  		ifr.MTU = int32(options.MTU)
   181  		return unix.IoctlSetIfreqMTU(socketFd, &ifr)
   182  	})
   183  	if err != nil {
   184  		return os.NewSyscallError("IoctlSetIfreqMTU", err)
   185  	}
   186  	if len(options.Inet4Address) > 0 {
   187  		for _, address := range options.Inet4Address {
   188  			ifReq := ifAliasReq{
   189  				Addr: unix.RawSockaddrInet4{
   190  					Len:    unix.SizeofSockaddrInet4,
   191  					Family: unix.AF_INET,
   192  					Addr:   address.Addr().As4(),
   193  				},
   194  				Dstaddr: unix.RawSockaddrInet4{
   195  					Len:    unix.SizeofSockaddrInet4,
   196  					Family: unix.AF_INET,
   197  					Addr:   address.Addr().As4(),
   198  				},
   199  				Mask: unix.RawSockaddrInet4{
   200  					Len:    unix.SizeofSockaddrInet4,
   201  					Family: unix.AF_INET,
   202  					Addr:   netip.MustParseAddr(net.IP(net.CIDRMask(address.Bits(), 32)).String()).As4(),
   203  				},
   204  			}
   205  			copy(ifReq.Name[:], name)
   206  			err = useSocket(unix.AF_INET, unix.SOCK_DGRAM, 0, func(socketFd int) error {
   207  				if _, _, errno := unix.Syscall(
   208  					syscall.SYS_IOCTL,
   209  					uintptr(socketFd),
   210  					uintptr(unix.SIOCAIFADDR),
   211  					uintptr(unsafe.Pointer(&ifReq)),
   212  				); errno != 0 {
   213  					return os.NewSyscallError("SIOCAIFADDR", errno)
   214  				}
   215  				return nil
   216  			})
   217  			if err != nil {
   218  				return err
   219  			}
   220  		}
   221  	}
   222  	if len(options.Inet6Address) > 0 {
   223  		for _, address := range options.Inet6Address {
   224  			ifReq6 := ifAliasReq6{
   225  				Addr: unix.RawSockaddrInet6{
   226  					Len:    unix.SizeofSockaddrInet6,
   227  					Family: unix.AF_INET6,
   228  					Addr:   address.Addr().As16(),
   229  				},
   230  				Mask: unix.RawSockaddrInet6{
   231  					Len:    unix.SizeofSockaddrInet6,
   232  					Family: unix.AF_INET6,
   233  					Addr:   netip.MustParseAddr(net.IP(net.CIDRMask(address.Bits(), 128)).String()).As16(),
   234  				},
   235  				Flags: IN6_IFF_NODAD | IN6_IFF_SECURED,
   236  				Lifetime: addrLifetime6{
   237  					Vltime: ND6_INFINITE_LIFETIME,
   238  					Pltime: ND6_INFINITE_LIFETIME,
   239  				},
   240  			}
   241  			if address.Bits() == 128 {
   242  				ifReq6.Dstaddr = unix.RawSockaddrInet6{
   243  					Len:    unix.SizeofSockaddrInet6,
   244  					Family: unix.AF_INET6,
   245  					Addr:   address.Addr().Next().As16(),
   246  				}
   247  			}
   248  			copy(ifReq6.Name[:], name)
   249  			err = useSocket(unix.AF_INET6, unix.SOCK_DGRAM, 0, func(socketFd int) error {
   250  				if _, _, errno := unix.Syscall(
   251  					syscall.SYS_IOCTL,
   252  					uintptr(socketFd),
   253  					uintptr(SIOCAIFADDR_IN6),
   254  					uintptr(unsafe.Pointer(&ifReq6)),
   255  				); errno != 0 {
   256  					return os.NewSyscallError("SIOCAIFADDR_IN6", errno)
   257  				}
   258  				return nil
   259  			})
   260  			if err != nil {
   261  				return err
   262  			}
   263  		}
   264  	}
   265  	if options.AutoRoute {
   266  		var routeRanges []netip.Prefix
   267  		routeRanges, err = options.BuildAutoRouteRanges()
   268  		for _, routeRange := range routeRanges {
   269  			if routeRange.Addr().Is4() {
   270  				err = addRoute(routeRange, options.Inet4Address[0].Addr())
   271  			} else {
   272  				err = addRoute(routeRange, options.Inet6Address[0].Addr())
   273  			}
   274  			if err != nil {
   275  				return E.Cause(err, "add route: ", routeRange)
   276  			}
   277  		}
   278  		flushDNSCache()
   279  	}
   280  	return nil
   281  }
   282  
   283  func useSocket(domain, typ, proto int, block func(socketFd int) error) error {
   284  	socketFd, err := unix.Socket(domain, typ, proto)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	defer unix.Close(socketFd)
   289  	return block(socketFd)
   290  }
   291  
   292  func addRoute(destination netip.Prefix, gateway netip.Addr) error {
   293  	routeMessage := route.RouteMessage{
   294  		Type:    unix.RTM_ADD,
   295  		Flags:   unix.RTF_UP | unix.RTF_STATIC | unix.RTF_GATEWAY,
   296  		Version: unix.RTM_VERSION,
   297  		Seq:     1,
   298  	}
   299  	if gateway.Is4() {
   300  		routeMessage.Addrs = []route.Addr{
   301  			syscall.RTAX_DST:     &route.Inet4Addr{IP: destination.Addr().As4()},
   302  			syscall.RTAX_NETMASK: &route.Inet4Addr{IP: netip.MustParseAddr(net.IP(net.CIDRMask(destination.Bits(), 32)).String()).As4()},
   303  			syscall.RTAX_GATEWAY: &route.Inet4Addr{IP: gateway.As4()},
   304  		}
   305  	} else {
   306  		routeMessage.Addrs = []route.Addr{
   307  			syscall.RTAX_DST:     &route.Inet6Addr{IP: destination.Addr().As16()},
   308  			syscall.RTAX_NETMASK: &route.Inet6Addr{IP: netip.MustParseAddr(net.IP(net.CIDRMask(destination.Bits(), 128)).String()).As16()},
   309  			syscall.RTAX_GATEWAY: &route.Inet6Addr{IP: gateway.As16()},
   310  		}
   311  	}
   312  	request, err := routeMessage.Marshal()
   313  	if err != nil {
   314  		return err
   315  	}
   316  	return useSocket(unix.AF_ROUTE, unix.SOCK_RAW, 0, func(socketFd int) error {
   317  		return common.Error(unix.Write(socketFd, request))
   318  	})
   319  }
   320  
   321  func flushDNSCache() {
   322  	shell.Exec("dscacheutil", "-flushcache").Start()
   323  }