mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
And reverts "refactor `mtu` to support setting IPv4/v6 separately" https://github.com/XTLS/Xray-core/pull/5891#issuecomment-4245677624 And fixes `autoOutboundsInterface` on Windows https://github.com/XTLS/Xray-core/pull/5887#issuecomment-4251719900 --------- Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
148 lines
3.5 KiB
Go
148 lines
3.5 KiB
Go
//go:build freebsd
|
|
|
|
package tun
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
_ "unsafe"
|
|
|
|
"golang.zx2c4.com/wireguard/tun"
|
|
"gvisor.dev/gvisor/pkg/buffer"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/xtls/xray-core/common/buf"
|
|
)
|
|
|
|
const tunHeaderSize = 4
|
|
|
|
//go:linkname procyield runtime.procyield
|
|
func procyield(cycles uint32)
|
|
|
|
type FreeBSDTun struct {
|
|
device tun.Device
|
|
mtu uint32
|
|
}
|
|
|
|
var _ Tun = (*FreeBSDTun)(nil)
|
|
var _ GVisorDevice = (*FreeBSDTun)(nil)
|
|
|
|
// NewTun builds new tun interface handler
|
|
func NewTun(options *Config) (Tun, error) {
|
|
tunDev, err := tun.CreateTUN(options.Name, int(options.MTU))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &FreeBSDTun{device: tunDev, mtu: options.MTU}, nil
|
|
}
|
|
|
|
func (t *FreeBSDTun) Start() error {
|
|
return nil
|
|
}
|
|
|
|
func (t *FreeBSDTun) Close() error {
|
|
return t.device.Close()
|
|
}
|
|
|
|
func (t *FreeBSDTun) Name() (string, error) {
|
|
return t.device.Name()
|
|
}
|
|
|
|
func (t *FreeBSDTun) Index() (int, error) {
|
|
name, err := t.Name()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return iface.Index, nil
|
|
}
|
|
|
|
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
|
func (t *FreeBSDTun) WritePacket(packet *stack.PacketBuffer) tcpip.Error {
|
|
// request memory to write from reusable buffer pool
|
|
b := buf.NewWithSize(int32(t.mtu) + tunHeaderSize)
|
|
defer b.Release()
|
|
|
|
// prepare Unix specific packet header
|
|
_, _ = b.Write([]byte{0x0, 0x0, 0x0, 0x0})
|
|
// copy the bytes of slices that compose the packet into the allocated buffer
|
|
for _, packetElement := range packet.AsSlices() {
|
|
_, _ = b.Write(packetElement)
|
|
}
|
|
// fill Unix specific header from the first raw packet byte, that we can access now
|
|
var family byte
|
|
switch b.Byte(4) >> 4 {
|
|
case 4:
|
|
family = unix.AF_INET
|
|
case 6:
|
|
family = unix.AF_INET6
|
|
default:
|
|
return &tcpip.ErrAborted{}
|
|
}
|
|
b.SetByte(3, family)
|
|
|
|
if _, err := t.device.File().Write(b.Bytes()); err != nil {
|
|
if errors.Is(err, unix.EAGAIN) {
|
|
return &tcpip.ErrWouldBlock{}
|
|
}
|
|
return &tcpip.ErrAborted{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ReadPacket implements GVisorDevice method to read one packet from the tun device
|
|
// It is expected that the method will not block, rather return ErrQueueEmpty when there is nothing on the line,
|
|
// which will make the stack call Wait which should implement desired push-back
|
|
func (t *FreeBSDTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
|
// request memory to write from reusable buffer pool
|
|
b := buf.NewWithSize(int32(t.mtu) + tunHeaderSize)
|
|
|
|
// read the bytes to the interface file
|
|
n, err := b.ReadFrom(t.device.File())
|
|
if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EINTR) {
|
|
b.Release()
|
|
return 0, nil, ErrQueueEmpty
|
|
}
|
|
if err != nil {
|
|
b.Release()
|
|
return 0, nil, err
|
|
}
|
|
|
|
// discard empty or sub-empty packets
|
|
if n <= tunHeaderSize {
|
|
b.Release()
|
|
return 0, nil, ErrQueueEmpty
|
|
}
|
|
|
|
// network protocol version from first byte of the raw packet, the one that follows Unix specific header
|
|
version := b.Byte(tunHeaderSize) >> 4
|
|
packetBuffer := buffer.MakeWithData(b.BytesFrom(tunHeaderSize))
|
|
return version, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
Payload: packetBuffer,
|
|
IsForwardedPacket: true,
|
|
OnRelease: func() {
|
|
b.Release()
|
|
},
|
|
}), nil
|
|
}
|
|
|
|
// Wait some cpu cycles
|
|
func (t *FreeBSDTun) Wait() {
|
|
procyield(1)
|
|
}
|
|
|
|
func (t *FreeBSDTun) newEndpoint() (stack.LinkEndpoint, error) {
|
|
return &LinkEndpoint{deviceMTU: t.mtu, device: t}, nil
|
|
}
|
|
|
|
func setinterface(network, address string, fd uintptr, iface *net.Interface) error {
|
|
return nil
|
|
}
|