Skip to content

Commit 9896627

Browse files
committed
TUN inbound: Add iOS support
1 parent f1aee0b commit 9896627

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

proxy/tun/README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ TUN interface support bridges the gap between network layer 3 and layer 7, intro
44

55
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. \
66
Primary targets are Linux based router devices. Like most popular OpenWRT option. \
7-
Although support for Windows is also implemented (see below).
7+
Support for Windows, macOS, and iOS is also implemented (see below).
88

99
## PLEASE READ FOLLOWING CAREFULLY
1010

@@ -194,3 +194,23 @@ sudo route add -inet6 -host 2606:4700:4700::1111 -iface utun10
194194
sudo route add -inet6 -host 2606:4700:4700::1001 -iface utun10
195195
```
196196
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.
197+
198+
## iOS SUPPORT
199+
200+
iOS uses the same utun packet format as macOS, but the file descriptor is provided by NetworkExtension instead of being created by Xray.
201+
202+
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray:
203+
204+
```swift
205+
guard let tunFd = packetFlow.value(forKey: "socket") as? Int32 else { return }
206+
setenv("xray.tun.fd", String(tunFd), 1)
207+
```
208+
209+
When this environment variable is set:
210+
- The `name` and `MTU` config values are ignored (the fd is already configured by NetworkExtension)
211+
- Xray will not close the fd on shutdown (ownership remains with NetworkExtension)
212+
213+
Build using gomobile for iOS framework integration:
214+
```
215+
gomobile bind -target=ios
216+
```

proxy/tun/tun_darwin.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"net"
99
"net/netip"
1010
"os"
11+
"strconv"
1112
"syscall"
1213
"unsafe"
1314

1415
"github.com/xtls/xray-core/common/buf"
16+
"github.com/xtls/xray-core/common/platform"
1517
"golang.org/x/sys/unix"
1618
"gvisor.dev/gvisor/pkg/buffer"
1719
"gvisor.dev/gvisor/pkg/tcpip"
@@ -38,13 +40,35 @@ func procyield(cycles uint32)
3840
type DarwinTun struct {
3941
tunFile *os.File
4042
options TunOptions
43+
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
4144
}
4245

4346
var _ Tun = (*DarwinTun)(nil)
4447
var _ GVisorTun = (*DarwinTun)(nil)
4548
var _ GVisorDevice = (*DarwinTun)(nil)
4649

4750
func NewTun(options TunOptions) (Tun, error) {
51+
// Check if fd is provided via environment (iOS mode)
52+
fdStr := platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "" })
53+
if fdStr != "" {
54+
// iOS: use provided fd from NetworkExtension
55+
fd, err := strconv.Atoi(fdStr)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
if err = unix.SetNonblock(fd, true); err != nil {
61+
return nil, err
62+
}
63+
64+
return &DarwinTun{
65+
tunFile: os.NewFile(uintptr(fd), "utun"),
66+
options: options,
67+
ownsFd: false,
68+
}, nil
69+
}
70+
71+
// macOS: create our own utun interface
4872
tunFile, err := open(options.Name)
4973
if err != nil {
5074
return nil, err
@@ -59,6 +83,7 @@ func NewTun(options TunOptions) (Tun, error) {
5983
return &DarwinTun{
6084
tunFile: tunFile,
6185
options: options,
86+
ownsFd: true,
6287
}, nil
6388
}
6489

@@ -67,7 +92,11 @@ func (t *DarwinTun) Start() error {
6792
}
6893

6994
func (t *DarwinTun) Close() error {
70-
return t.tunFile.Close()
95+
if t.ownsFd {
96+
return t.tunFile.Close()
97+
}
98+
// iOS: don't close the fd, it's owned by NetworkExtension
99+
return nil
71100
}
72101

73102
// WritePacket implements GVisorDevice method to write one packet to the tun device

0 commit comments

Comments
 (0)