mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
* Proxy: TUN: Enhance Darwin interface support. - reduce number of actions done to create/configure the interface in the system - assign synthetic static link-local ipv4/ipv6 addresses to the interface, that are required by the OS for the routing to work - make tun_darwin_endpoint be implemented significantly more similar to tun_windows_enpoint, preparing them for potential unification * Proxy: TUN: Unify Darwin/Windows endpoint, which are now extremely similar, into one GVisorEndpoint. Making darwin/windows tun implement GVisorDevice with simple readpacket/writepacket methods that GVisorEndpoint untilise
156 lines
3.6 KiB
Go
156 lines
3.6 KiB
Go
package tun
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
)
|
|
|
|
var ErrQueueEmpty = errors.New("queue is empty")
|
|
|
|
type GVisorDevice interface {
|
|
WritePacket(packet *stack.PacketBuffer) tcpip.Error
|
|
ReadPacket() (byte, *stack.PacketBuffer, error)
|
|
Wait()
|
|
}
|
|
|
|
// LinkEndpoint implements GVisor stack.LinkEndpoint
|
|
var _ stack.LinkEndpoint = (*LinkEndpoint)(nil)
|
|
|
|
type LinkEndpoint struct {
|
|
deviceMTU uint32
|
|
device GVisorDevice
|
|
dispatcherCancel context.CancelFunc
|
|
}
|
|
|
|
func (e *LinkEndpoint) MTU() uint32 {
|
|
return e.deviceMTU
|
|
}
|
|
|
|
func (e *LinkEndpoint) SetMTU(_ uint32) {
|
|
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
|
}
|
|
|
|
func (e *LinkEndpoint) MaxHeaderLength() uint16 {
|
|
return 0
|
|
}
|
|
|
|
func (e *LinkEndpoint) LinkAddress() tcpip.LinkAddress {
|
|
return ""
|
|
}
|
|
|
|
func (e *LinkEndpoint) SetLinkAddress(_ tcpip.LinkAddress) {
|
|
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
|
}
|
|
|
|
func (e *LinkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
|
return stack.CapabilityRXChecksumOffload
|
|
}
|
|
|
|
func (e *LinkEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
|
if e.dispatcherCancel != nil {
|
|
e.dispatcherCancel()
|
|
e.dispatcherCancel = nil
|
|
}
|
|
|
|
if dispatcher != nil {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go e.dispatchLoop(ctx, dispatcher)
|
|
e.dispatcherCancel = cancel
|
|
}
|
|
}
|
|
|
|
func (e *LinkEndpoint) IsAttached() bool {
|
|
return e.dispatcherCancel != nil
|
|
}
|
|
|
|
func (e *LinkEndpoint) Wait() {
|
|
|
|
}
|
|
|
|
func (e *LinkEndpoint) ARPHardwareType() header.ARPHardwareType {
|
|
return header.ARPHardwareNone
|
|
}
|
|
|
|
func (e *LinkEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
|
// tun interface doesn't have link layer header, it will be added by the OS
|
|
}
|
|
|
|
func (e *LinkEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
|
|
return true
|
|
}
|
|
|
|
func (e *LinkEndpoint) Close() {
|
|
if e.dispatcherCancel != nil {
|
|
e.dispatcherCancel()
|
|
e.dispatcherCancel = nil
|
|
}
|
|
}
|
|
|
|
func (e *LinkEndpoint) SetOnCloseAction(_ func()) {
|
|
|
|
}
|
|
|
|
func (e *LinkEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
|
var n int
|
|
var err tcpip.Error
|
|
|
|
for _, packetBuffer := range packetBufferList.AsSlice() {
|
|
err = e.device.WritePacket(packetBuffer)
|
|
if err != nil {
|
|
return n, &tcpip.ErrAborted{}
|
|
}
|
|
n++
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (e *LinkEndpoint) dispatchLoop(ctx context.Context, dispatcher stack.NetworkDispatcher) {
|
|
var networkProtocolNumber tcpip.NetworkProtocolNumber
|
|
var version byte
|
|
var packet *stack.PacketBuffer
|
|
var err error
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
version, packet, err = e.device.ReadPacket()
|
|
// on "queue empty", ask device to yield slightly and continue
|
|
if errors.Is(err, ErrQueueEmpty) {
|
|
e.device.Wait()
|
|
continue
|
|
}
|
|
// stop dispatcher loop on any other interface failure
|
|
if err != nil {
|
|
e.Attach(nil)
|
|
return
|
|
}
|
|
|
|
// extract network protocol number from the packet first byte
|
|
// (which is returned separately, since it is so incredibly hard to extract one byte from
|
|
// stack.PacketBuffer without additional memory allocation and full copying it back and forth)
|
|
switch version {
|
|
case 4:
|
|
networkProtocolNumber = header.IPv4ProtocolNumber
|
|
case 6:
|
|
networkProtocolNumber = header.IPv6ProtocolNumber
|
|
default:
|
|
// discard unknown network protocol packet
|
|
packet.DecRef()
|
|
continue
|
|
}
|
|
|
|
// dispatch the buffer to the stack
|
|
dispatcher.DeliverNetworkPacket(networkProtocolNumber, packet)
|
|
// signal the buffer that it can be released
|
|
packet.DecRef()
|
|
}
|
|
}
|
|
}
|