@@ -4,8 +4,11 @@ package tun
44
55import (
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
2428var _ Tun = (* LinuxTun )(nil )
2529
2630// NewTun builds new tun interface handler (linux specific)
2731func 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
49113func 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
95159func (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
105173func (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