Skip to content

Commit 002c85e

Browse files
authored
TUN inbound: Support env XRAY_TUN_FD on Linux as well (#6338)
#6338 (comment)
1 parent e6428fa commit 002c85e

1 file changed

Lines changed: 73 additions & 3 deletions

File tree

proxy/tun/tun_linux.go

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ package tun
44

55
import (
66
"net"
7+
"strconv"
78

89
"github.com/vishvananda/netlink"
10+
"github.com/xtls/xray-core/common/errors"
11+
"github.com/xtls/xray-core/common/platform"
912
"golang.org/x/sys/unix"
1013
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
1114
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -18,19 +21,32 @@ type LinuxTun struct {
1821
tunFd int
1922
tunLink netlink.Link
2023
options *Config
24+
ownsTun bool
2125
}
2226

2327
// LinuxTun implements Tun
2428
var _ Tun = (*LinuxTun)(nil)
2529

2630
// NewTun builds new tun interface handler (linux specific)
2731
func NewTun(options *Config) (Tun, error) {
28-
tunFd, err := open(options.Name)
32+
tunFd, tunLink, fdProvided, err := openFromEnv(options.Name)
33+
if err != nil {
34+
return nil, err
35+
}
36+
if fdProvided {
37+
return &LinuxTun{
38+
tunFd: tunFd,
39+
tunLink: tunLink,
40+
options: options,
41+
}, nil
42+
}
43+
44+
tunFd, err = open(options.Name)
2945
if err != nil {
3046
return nil, err
3147
}
3248

33-
tunLink, err := setup(options.Name, int(options.MTU))
49+
tunLink, err = setup(options.Name, int(options.MTU))
3450
if err != nil {
3551
_ = unix.Close(tunFd)
3652
return nil, err
@@ -40,11 +56,59 @@ func NewTun(options *Config) (Tun, error) {
4056
tunFd: tunFd,
4157
tunLink: tunLink,
4258
options: options,
59+
ownsTun: true,
4360
}
4461

4562
return linuxTun, nil
4663
}
4764

65+
func openFromEnv(expectedName string) (int, netlink.Link, bool, error) {
66+
fdStr := platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "" })
67+
if fdStr == "" {
68+
return -1, nil, false, nil
69+
}
70+
71+
fd, err := strconv.Atoi(fdStr)
72+
if err != nil {
73+
return -1, nil, true, errors.New("invalid ", platform.TunFdKey).Base(err)
74+
}
75+
if fd < 3 {
76+
return -1, nil, true, errors.New("invalid ", platform.TunFdKey, ": file descriptor must be >= 3")
77+
}
78+
79+
ifr, err := unix.NewIfreq("")
80+
if err != nil {
81+
return -1, nil, true, err
82+
}
83+
if err = unix.IoctlIfreq(fd, unix.TUNGETIFF, ifr); err != nil {
84+
return -1, nil, true, err
85+
}
86+
87+
flags := ifr.Uint16()
88+
if flags&unix.IFF_TUN == 0 {
89+
return -1, nil, true, errors.New("invalid ", platform.TunFdKey, ": file descriptor is not a TUN device")
90+
}
91+
if flags&unix.IFF_NO_PI == 0 {
92+
return -1, nil, true, errors.New("invalid ", platform.TunFdKey, ": TUN device must use IFF_NO_PI")
93+
}
94+
95+
actualName := ifr.Name()
96+
if expectedName != "" && actualName != expectedName {
97+
return -1, nil, true, errors.New("invalid ", platform.TunFdKey, ": TUN device name ", actualName, " does not match configured name ", expectedName)
98+
}
99+
100+
tunLink, err := netlink.LinkByName(actualName)
101+
if err != nil {
102+
return -1, nil, true, err
103+
}
104+
105+
if err = unix.SetNonblock(fd, true); err != nil {
106+
return -1, nil, true, err
107+
}
108+
109+
return fd, tunLink, true, nil
110+
}
111+
48112
// open the file that implements tun interface in the OS
49113
func open(name string) (int, error) {
50114
fd, err := unix.Open("/dev/net/tun", unix.O_RDWR, 0)
@@ -93,6 +157,10 @@ func setup(name string, MTU int) (netlink.Link, error) {
93157

94158
// Start is called by handler to bring tun interface to life
95159
func (t *LinuxTun) Start() error {
160+
if !t.ownsTun {
161+
return nil
162+
}
163+
96164
err := netlink.LinkSetUp(t.tunLink)
97165
if err != nil {
98166
return err
@@ -103,7 +171,9 @@ func (t *LinuxTun) Start() error {
103171

104172
// Close is called to shut down the tun interface
105173
func (t *LinuxTun) Close() error {
106-
_ = netlink.LinkSetDown(t.tunLink)
174+
if t.ownsTun {
175+
_ = netlink.LinkSetDown(t.tunLink)
176+
}
107177
_ = unix.Close(t.tunFd)
108178

109179
return nil

0 commit comments

Comments
 (0)