Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion proxy/tun/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
```
31 changes: 30 additions & 1 deletion proxy/tun/tun_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -38,13 +40,35 @@ 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)
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
Expand All @@ -59,6 +83,7 @@ func NewTun(options TunOptions) (Tun, error) {
return &DarwinTun{
tunFile: tunFile,
options: options,
ownsFd: true,
}, nil
}

Expand All @@ -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
Expand Down