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 }