mirror of
				https://github.com/SagerNet/sing-tun.git
				synced 2025-11-01 04:12:50 +08:00 
			
		
		
		
	Add darwin support
This commit is contained in:
		| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| Simple transparent proxy library. | Simple transparent proxy library. | ||||||
|  |  | ||||||
| Currently only for linux and windows. | For Linux, Windows and macOS. | ||||||
|  |  | ||||||
| ## License | ## License | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import ( | |||||||
| 	"net/netip" | 	"net/netip" | ||||||
| 	"os" | 	"os" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
| 	"github.com/sagernet/sing/common" | 	"github.com/sagernet/sing/common" | ||||||
| 	E "github.com/sagernet/sing/common/exceptions" | 	E "github.com/sagernet/sing/common/exceptions" | ||||||
| @@ -13,7 +14,6 @@ import ( | |||||||
|  |  | ||||||
| 	"golang.org/x/net/route" | 	"golang.org/x/net/route" | ||||||
| 	"golang.org/x/sys/unix" | 	"golang.org/x/sys/unix" | ||||||
| 	"syscall" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type networkUpdateMonitor struct { | type networkUpdateMonitor struct { | ||||||
| @@ -47,7 +47,7 @@ func (m *networkUpdateMonitor) Start() error { | |||||||
| func (m *networkUpdateMonitor) loopUpdate() { | func (m *networkUpdateMonitor) loopUpdate() { | ||||||
| 	rawConn, err := m.routeSocket.SyscallConn() | 	rawConn, err := m.routeSocket.SyscallConn() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		m.errorHandler.NewError(context.Background(), err) | 		m.errorHandler.NewError(context.Background(), E.Cause(err, "create raw route connection")) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	for { | 	for { | ||||||
| @@ -66,7 +66,7 @@ func (m *networkUpdateMonitor) loopUpdate() { | |||||||
| 		m.emit() | 		m.emit() | ||||||
| 	} | 	} | ||||||
| 	if err != syscall.EAGAIN { | 	if err != syscall.EAGAIN { | ||||||
| 		m.errorHandler.NewError(context.Background(), err) | 		m.errorHandler.NewError(context.Background(), E.Cause(err, "read route message")) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										392
									
								
								tun_darwin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								tun_darwin.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,392 @@ | |||||||
|  | package tun | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net" | ||||||
|  | 	"net/netip" | ||||||
|  | 	"os" | ||||||
|  | 	"syscall" | ||||||
|  | 	"unsafe" | ||||||
|  |  | ||||||
|  | 	"github.com/sagernet/sing/common" | ||||||
|  | 	"github.com/sagernet/sing/common/buf" | ||||||
|  | 	E "github.com/sagernet/sing/common/exceptions" | ||||||
|  | 	"github.com/sagernet/sing/common/rw" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/net/route" | ||||||
|  | 	"golang.org/x/sys/unix" | ||||||
|  | 	gBuffer "gvisor.dev/gvisor/pkg/buffer" | ||||||
|  | 	"gvisor.dev/gvisor/pkg/tcpip" | ||||||
|  | 	"gvisor.dev/gvisor/pkg/tcpip/header" | ||||||
|  | 	"gvisor.dev/gvisor/pkg/tcpip/stack" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type NativeTun struct { | ||||||
|  | 	tunFd        uintptr | ||||||
|  | 	tunFile      *os.File | ||||||
|  | 	inet4Address tcpip.Address | ||||||
|  | 	inet6Address tcpip.Address | ||||||
|  | 	mtu          uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) { | ||||||
|  | 	ifIndex := -1 | ||||||
|  | 	_, err := fmt.Sscanf(name, "utun%d", &ifIndex) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, E.New("bad tun name: ", name) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tunFd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = configure(tunFd, ifIndex, name, inet4Address, inet6Address, mtu, autoRoute) | ||||||
|  | 	if err != nil { | ||||||
|  | 		unix.Close(tunFd) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &NativeTun{ | ||||||
|  | 		tunFd:        uintptr(tunFd), | ||||||
|  | 		tunFile:      os.NewFile(uintptr(tunFd), "utun"), | ||||||
|  | 		inet4Address: tcpip.Address(inet4Address.Addr().AsSlice()), | ||||||
|  | 		inet6Address: tcpip.Address(inet6Address.Addr().AsSlice()), | ||||||
|  | 		mtu:          mtu, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, error) { | ||||||
|  | 	return &DarwinEndpoint{tun: t}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *NativeTun) Close() error { | ||||||
|  | 	return t.tunFile.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ stack.LinkEndpoint = (*DarwinEndpoint)(nil) | ||||||
|  |  | ||||||
|  | type DarwinEndpoint struct { | ||||||
|  | 	tun        *NativeTun | ||||||
|  | 	dispatcher stack.NetworkDispatcher | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) MTU() uint32 { | ||||||
|  | 	return e.tun.mtu | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) MaxHeaderLength() uint16 { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) LinkAddress() tcpip.LinkAddress { | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) Capabilities() stack.LinkEndpointCapabilities { | ||||||
|  | 	return stack.CapabilityNone | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) Attach(dispatcher stack.NetworkDispatcher) { | ||||||
|  | 	if dispatcher == nil && e.dispatcher != nil { | ||||||
|  | 		e.dispatcher = nil | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if dispatcher != nil && e.dispatcher == nil { | ||||||
|  | 		e.dispatcher = dispatcher | ||||||
|  | 		go e.dispatchLoop() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) dispatchLoop() { | ||||||
|  | 	_buffer := buf.StackNewSize(int(e.tun.mtu) + 4) | ||||||
|  | 	defer common.KeepAlive(_buffer) | ||||||
|  | 	buffer := common.Dup(_buffer) | ||||||
|  | 	defer buffer.Release() | ||||||
|  | 	data := buffer.FreeBytes() | ||||||
|  | 	for { | ||||||
|  | 		n, err := e.tun.tunFile.Read(data) | ||||||
|  | 		if err != nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		packet := buf.NewSize(n - 4) | ||||||
|  | 		common.Must1(packet.Write(data[4:n])) | ||||||
|  | 		pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ | ||||||
|  | 			Payload:           gBuffer.NewWithData(packet.Bytes()), | ||||||
|  | 			IsForwardedPacket: true, | ||||||
|  | 			OnRelease:         packet.Release, | ||||||
|  | 		}) | ||||||
|  | 		var p tcpip.NetworkProtocolNumber | ||||||
|  | 		ipHeader, ok := pkt.Data().PullUp(1) | ||||||
|  | 		if !ok { | ||||||
|  | 			pkt.DecRef() | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		switch header.IPVersion(ipHeader) { | ||||||
|  | 		case header.IPv4Version: | ||||||
|  | 			p = header.IPv4ProtocolNumber | ||||||
|  | 			if header.IPv4(packet.Bytes()).DestinationAddress() == e.tun.inet4Address { | ||||||
|  | 				_, err = e.tun.tunFile.Write(data[:n]) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		case header.IPv6Version: | ||||||
|  | 			p = header.IPv6ProtocolNumber | ||||||
|  | 			if header.IPv6(packet.Bytes()).DestinationAddress() == e.tun.inet6Address { | ||||||
|  | 				_, err = e.tun.tunFile.Write(data[:n]) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		dispatcher := e.dispatcher | ||||||
|  | 		if dispatcher == nil { | ||||||
|  | 			pkt.DecRef() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		dispatcher.DeliverNetworkPacket(p, pkt) | ||||||
|  | 		pkt.DecRef() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) IsAttached() bool { | ||||||
|  | 	return e.dispatcher != nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) Wait() { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) ARPHardwareType() header.ARPHardwareType { | ||||||
|  | 	return header.ARPHardwareNone | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) AddHeader(buffer *stack.PacketBuffer) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *DarwinEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) { | ||||||
|  | 	_packetHeader := buf.StackNewSize(4) | ||||||
|  | 	defer common.KeepAlive(_packetHeader) | ||||||
|  | 	packetHeader := common.Dup(_packetHeader) | ||||||
|  | 	defer packetHeader.Release() | ||||||
|  | 	var n int | ||||||
|  | 	for _, packet := range packetBufferList.AsSlice() { | ||||||
|  | 		packetHeader.FullReset() | ||||||
|  | 		packetHeader.WriteZeroN(3) | ||||||
|  | 		switch packet.NetworkProtocolNumber { | ||||||
|  | 		case header.IPv4ProtocolNumber: | ||||||
|  | 			packetHeader.WriteByte(unix.AF_INET) | ||||||
|  | 		case header.IPv6ProtocolNumber: | ||||||
|  | 			packetHeader.WriteByte(unix.AF_INET6) | ||||||
|  | 		} | ||||||
|  | 		_, err := rw.WriteV(e.tun.tunFd, append([][]byte{packetHeader.Bytes()}, packet.Slices()...)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return n, &tcpip.ErrAborted{} | ||||||
|  | 		} | ||||||
|  | 		n++ | ||||||
|  | 	} | ||||||
|  | 	return n, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const utunControlName = "com.apple.net.utun_control" | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	SIOCAIFADDR_IN6       = 2155899162 // netinet6/in6_var.h | ||||||
|  | 	IN6_IFF_NODAD         = 0x0020     // netinet6/in6_var.h | ||||||
|  | 	IN6_IFF_SECURED       = 0x0400     // netinet6/in6_var.h | ||||||
|  | 	ND6_INFINITE_LIFETIME = 0xFFFFFFFF // netinet6/nd6.h | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ifAliasReq struct { | ||||||
|  | 	Name    [unix.IFNAMSIZ]byte | ||||||
|  | 	Addr    unix.RawSockaddrInet4 | ||||||
|  | 	Dstaddr unix.RawSockaddrInet4 | ||||||
|  | 	Mask    unix.RawSockaddrInet4 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ifAliasReq6 struct { | ||||||
|  | 	Name     [16]byte | ||||||
|  | 	Addr     unix.RawSockaddrInet6 | ||||||
|  | 	Dstaddr  unix.RawSockaddrInet6 | ||||||
|  | 	Mask     unix.RawSockaddrInet6 | ||||||
|  | 	Flags    uint32 | ||||||
|  | 	Lifetime addrLifetime6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type addrLifetime6 struct { | ||||||
|  | 	Expire    float64 | ||||||
|  | 	Preferred float64 | ||||||
|  | 	Vltime    uint32 | ||||||
|  | 	Pltime    uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) error { | ||||||
|  | 	ctlInfo := &unix.CtlInfo{} | ||||||
|  | 	copy(ctlInfo.Name[:], utunControlName) | ||||||
|  | 	err := unix.IoctlCtlInfo(tunFd, ctlInfo) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = unix.Connect(tunFd, &unix.SockaddrCtl{ | ||||||
|  | 		ID:   ctlInfo.Id, | ||||||
|  | 		Unit: uint32(ifIndex) + 1, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = unix.SetNonblock(tunFd, true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = useSocket(unix.AF_INET, unix.SOCK_DGRAM, 0, func(socketFd int) error { | ||||||
|  | 		var ifr unix.IfreqMTU | ||||||
|  | 		copy(ifr.Name[:], name) | ||||||
|  | 		ifr.MTU = int32(mtu) | ||||||
|  | 		return unix.IoctlSetIfreqMTU(socketFd, &ifr) | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if inet4Address.IsValid() { | ||||||
|  | 		ifReq := ifAliasReq{ | ||||||
|  | 			Addr: unix.RawSockaddrInet4{ | ||||||
|  | 				Len:    unix.SizeofSockaddrInet4, | ||||||
|  | 				Family: unix.AF_INET, | ||||||
|  | 				Addr:   inet4Address.Addr().As4(), | ||||||
|  | 			}, | ||||||
|  | 			Dstaddr: unix.RawSockaddrInet4{ | ||||||
|  | 				Len:    unix.SizeofSockaddrInet4, | ||||||
|  | 				Family: unix.AF_INET, | ||||||
|  | 				Addr:   inet4Address.Addr().As4(), | ||||||
|  | 			}, | ||||||
|  | 			Mask: unix.RawSockaddrInet4{ | ||||||
|  | 				Len:    unix.SizeofSockaddrInet4, | ||||||
|  | 				Family: unix.AF_INET, | ||||||
|  | 				Addr:   netip.MustParseAddr(net.IP(net.CIDRMask(inet4Address.Bits(), 32)).String()).As4(), | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		copy(ifReq.Name[:], name) | ||||||
|  | 		err = useSocket(unix.AF_INET, unix.SOCK_DGRAM, 0, func(socketFd int) error { | ||||||
|  | 			if _, _, errno := unix.Syscall( | ||||||
|  | 				syscall.SYS_IOCTL, | ||||||
|  | 				uintptr(socketFd), | ||||||
|  | 				uintptr(unix.SIOCAIFADDR), | ||||||
|  | 				uintptr(unsafe.Pointer(&ifReq)), | ||||||
|  | 			); errno != 0 { | ||||||
|  | 				return os.NewSyscallError("SIOCAIFADDR", errno) | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if inet6Address.IsValid() { | ||||||
|  | 		ifReq6 := ifAliasReq6{ | ||||||
|  | 			Addr: unix.RawSockaddrInet6{ | ||||||
|  | 				Len:    unix.SizeofSockaddrInet6, | ||||||
|  | 				Family: unix.AF_INET6, | ||||||
|  | 				Addr:   inet6Address.Addr().As16(), | ||||||
|  | 			}, | ||||||
|  | 			Mask: unix.RawSockaddrInet6{ | ||||||
|  | 				Len:    unix.SizeofSockaddrInet6, | ||||||
|  | 				Family: unix.AF_INET6, | ||||||
|  | 				Addr:   netip.MustParseAddr(net.IP(net.CIDRMask(inet6Address.Bits(), 128)).String()).As16(), | ||||||
|  | 			}, | ||||||
|  | 			Flags: IN6_IFF_NODAD | IN6_IFF_SECURED, | ||||||
|  | 			Lifetime: addrLifetime6{ | ||||||
|  | 				Vltime: ND6_INFINITE_LIFETIME, | ||||||
|  | 				Pltime: ND6_INFINITE_LIFETIME, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		if inet6Address.Bits() == 128 { | ||||||
|  | 			ifReq6.Dstaddr = unix.RawSockaddrInet6{ | ||||||
|  | 				Len:    unix.SizeofSockaddrInet6, | ||||||
|  | 				Family: unix.AF_INET6, | ||||||
|  | 				Addr:   inet6Address.Addr().Next().As16(), | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		copy(ifReq6.Name[:], name) | ||||||
|  | 		err = useSocket(unix.AF_INET6, unix.SOCK_DGRAM, 0, func(socketFd int) error { | ||||||
|  | 			if _, _, errno := unix.Syscall( | ||||||
|  | 				syscall.SYS_IOCTL, | ||||||
|  | 				uintptr(socketFd), | ||||||
|  | 				uintptr(SIOCAIFADDR_IN6), | ||||||
|  | 				uintptr(unsafe.Pointer(&ifReq6)), | ||||||
|  | 			); errno != 0 { | ||||||
|  | 				return os.NewSyscallError("SIOCAIFADDR_IN6", errno) | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if autoRoute { | ||||||
|  | 		if inet4Address.IsValid() { | ||||||
|  | 			for _, subnet := range []netip.Prefix{ | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 0, 0, 0}), 8), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{2, 0, 0, 0}), 7), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{4, 0, 0, 0}), 6), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{8, 0, 0, 0}), 5), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{16, 0, 0, 0}), 4), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{32, 0, 0, 0}), 3), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{64, 0, 0, 0}), 2), | ||||||
|  | 				netip.PrefixFrom(netip.AddrFrom4([4]byte{128, 0, 0, 0}), 1), | ||||||
|  | 			} { | ||||||
|  | 				err = addRoute(subnet, inet4Address.Addr()) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if inet6Address.IsValid() { | ||||||
|  | 			subnet := netip.PrefixFrom(netip.AddrFrom16([16]byte{32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), 3) | ||||||
|  | 			err = addRoute(subnet, inet6Address.Addr()) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func useSocket(domain, typ, proto int, block func(socketFd int) error) error { | ||||||
|  | 	socketFd, err := unix.Socket(domain, typ, proto) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer unix.Close(socketFd) | ||||||
|  | 	return block(socketFd) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func addRoute(destination netip.Prefix, gateway netip.Addr) error { | ||||||
|  | 	routeMessage := route.RouteMessage{ | ||||||
|  | 		Type:    unix.RTM_ADD, | ||||||
|  | 		Flags:   unix.RTF_UP | unix.RTF_STATIC | unix.RTF_GATEWAY, | ||||||
|  | 		Version: unix.RTM_VERSION, | ||||||
|  | 		Seq:     1, | ||||||
|  | 	} | ||||||
|  | 	if gateway.Is4() { | ||||||
|  | 		routeMessage.Addrs = []route.Addr{ | ||||||
|  | 			syscall.RTAX_DST:     &route.Inet4Addr{IP: destination.Addr().As4()}, | ||||||
|  | 			syscall.RTAX_NETMASK: &route.Inet4Addr{IP: netip.MustParseAddr(net.IP(net.CIDRMask(destination.Bits(), 32)).String()).As4()}, | ||||||
|  | 			syscall.RTAX_GATEWAY: &route.Inet4Addr{IP: gateway.As4()}, | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		routeMessage.Addrs = []route.Addr{ | ||||||
|  | 			syscall.RTAX_DST:     &route.Inet6Addr{IP: destination.Addr().As16()}, | ||||||
|  | 			syscall.RTAX_NETMASK: &route.Inet6Addr{IP: netip.MustParseAddr(net.IP(net.CIDRMask(destination.Bits(), 128)).String()).As16()}, | ||||||
|  | 			syscall.RTAX_GATEWAY: &route.Inet6Addr{IP: gateway.As16()}, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	request, err := routeMessage.Marshal() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return useSocket(unix.AF_ROUTE, unix.SOCK_RAW, 0, func(socketFd int) error { | ||||||
|  | 		return common.Error(unix.Write(socketFd, request)) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| //go:build no_gvisor || !(linux || windows) | //go:build no_gvisor || !(linux || windows || darwin) | ||||||
|  |  | ||||||
| package tun | package tun | ||||||
|  |  | ||||||
|   | |||||||
| @@ -308,7 +308,7 @@ func (e *WintunEndpoint) Attach(dispatcher stack.NetworkDispatcher) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (e *WintunEndpoint) dispatchLoop() { | func (e *WintunEndpoint) dispatchLoop() { | ||||||
| 	_buffer := buf.StackNewPacket() | 	_buffer := buf.StackNewSize(int(e.tun.mtu)) | ||||||
| 	defer common.KeepAlive(_buffer) | 	defer common.KeepAlive(_buffer) | ||||||
| 	buffer := common.Dup(_buffer) | 	buffer := common.Dup(_buffer) | ||||||
| 	defer buffer.Release() | 	defer buffer.Release() | ||||||
| @@ -339,7 +339,12 @@ func (e *WintunEndpoint) dispatchLoop() { | |||||||
| 		default: | 		default: | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		e.dispatcher.DeliverNetworkPacket(p, pkt) | 		dispatcher := e.dispatcher | ||||||
|  | 		if dispatcher == nil { | ||||||
|  | 			pkt.DecRef() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		dispatcher.DeliverNetworkPacket(p, pkt) | ||||||
| 		pkt.DecRef() | 		pkt.DecRef() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 世界
					世界