TUN inbound: Add iOS support (#5612)

And https://github.com/XTLS/Xray-core/pull/5612#issuecomment-3799070838
This commit is contained in:
Evozi Team
2026-01-26 20:43:10 +08:00
committed by GitHub
parent 9a04eecaf9
commit c3af657c0e
2 changed files with 67 additions and 2 deletions

View File

@@ -4,7 +4,7 @@ TUN interface support bridges the gap between network layer 3 and layer 7, intro
This functionality is targeted to assist applications/end devices that don't have proxy support, or can't run external applications (like Smart TV's). Making it possible to run Xray proxy right on network edge devices (routers) with support to route raw network traffic. \
Primary targets are Linux based router devices. Like most popular OpenWRT option. \
Although support for Windows is also implemented (see below).
Support for Windows, macOS, Android and iOS is also implemented (see below).
## PLEASE READ FOLLOWING CAREFULLY
@@ -194,3 +194,39 @@ sudo route add -inet6 -host 2606:4700:4700::1111 -iface utun10
sudo route add -inet6 -host 2606:4700:4700::1001 -iface utun10
```
Important to remember that everything written above about Linux routing concept, also apply to Mac OS X. If you simply route default route through utun interface, that will result network loop and immediate network failure.
## ANDROID SUPPORT
Android uses the VpnService API which provides a TUN file descriptor to the application.
Obtain the fd from VpnService:
```kotlin
val tunFd = vpnInterface.fd
```
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray. This can be done from Kotlin/Java or by exposing a Go function via gomobile bindings.
Build using gomobile for Android library integration:
```
gomobile bind -target=android
```
## iOS SUPPORT
iOS uses the same utun packet format as macOS, but the file descriptor is provided by NetworkExtension.
Obtain the fd from NetworkExtension:
```swift
var buf = [CChar](repeating: 0, count: Int(IFNAMSIZ))
let utunPrefix = "utun".utf8CString.dropLast()
let tunFd = ((0 ... 1024).first { (_ fd: Int32) -> Bool in var len = socklen_t(buf.count)
return getsockopt(fd, 2, 2, &buf, &len) == 0 && buf.starts(with: utunPrefix)
}!
```
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray. This can be done from Swift/Objective-C or by exposing a Go function via gomobile bindings.
Build using gomobile for iOS framework integration:
```
gomobile bind -target=ios
```

View File

@@ -8,10 +8,12 @@ import (
"net"
"net/netip"
"os"
"strconv"
"syscall"
"unsafe"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/platform"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -38,6 +40,7 @@ func procyield(cycles uint32)
type DarwinTun struct {
tunFile *os.File
options TunOptions
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
}
var _ Tun = (*DarwinTun)(nil)
@@ -45,6 +48,27 @@ var _ GVisorTun = (*DarwinTun)(nil)
var _ GVisorDevice = (*DarwinTun)(nil)
func NewTun(options TunOptions) (Tun, error) {
// Check if fd is provided via environment (iOS mode)
fdStr := platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "" })
if fdStr != "" {
// iOS: use provided fd from NetworkExtension
fd, err := strconv.Atoi(fdStr)
if err != nil {
return nil, err
}
if err = unix.SetNonblock(fd, true); err != nil {
return nil, err
}
return &DarwinTun{
tunFile: os.NewFile(uintptr(fd), "utun"),
options: options,
ownsFd: false,
}, nil
}
// macOS: create our own utun interface
tunFile, err := open(options.Name)
if err != nil {
return nil, err
@@ -59,6 +83,7 @@ func NewTun(options TunOptions) (Tun, error) {
return &DarwinTun{
tunFile: tunFile,
options: options,
ownsFd: true,
}, nil
}
@@ -67,7 +92,11 @@ func (t *DarwinTun) Start() error {
}
func (t *DarwinTun) Close() error {
return t.tunFile.Close()
if t.ownsFd {
return t.tunFile.Close()
}
// iOS: don't close the fd, it's owned by NetworkExtension
return nil
}
// WritePacket implements GVisorDevice method to write one packet to the tun device