4
4
package overlay
5
5
6
6
import (
7
+ "bytes"
8
+ "errors"
7
9
"fmt"
8
10
"io"
11
+ "io/fs"
9
12
"net"
10
13
"os"
11
14
"os/exec"
12
- "regexp"
13
15
"strconv"
14
- "strings"
16
+ "syscall"
17
+ "unsafe"
15
18
16
19
"github.com/sirupsen/logrus"
17
20
"github.com/slackhq/nebula/cidr"
18
21
"github.com/slackhq/nebula/iputil"
19
22
)
20
23
21
- var deviceNameRE = regexp .MustCompile (`^tun[0-9]+$` )
24
+ const (
25
+ // FIODGNAME is defined in sys/sys/filio.h on FreeBSD
26
+ // For 32-bit systems, use FIODGNAME_32 (not defined in this file: 0x80086678)
27
+ FIODGNAME = 0x80106678
28
+ )
29
+
30
+ type fiodgnameArg struct {
31
+ length int32
32
+ pad [4 ]byte
33
+ buf unsafe.Pointer
34
+ }
35
+
36
+ type ifreqRename struct {
37
+ Name [16 ]byte
38
+ Data uintptr
39
+ }
40
+
41
+ type ifreqDestroy struct {
42
+ Name [16 ]byte
43
+ pad [16 ]byte
44
+ }
22
45
23
46
type tun struct {
24
47
Device string
@@ -33,8 +56,23 @@ type tun struct {
33
56
34
57
func (t * tun ) Close () error {
35
58
if t .ReadWriteCloser != nil {
36
- return t .ReadWriteCloser .Close ()
59
+ if err := t .ReadWriteCloser .Close (); err != nil {
60
+ return err
61
+ }
62
+
63
+ s , err := syscall .Socket (syscall .AF_INET , syscall .SOCK_DGRAM , syscall .IPPROTO_IP )
64
+ if err != nil {
65
+ return err
66
+ }
67
+ defer syscall .Close (s )
68
+
69
+ ifreq := ifreqDestroy {Name : t .deviceBytes ()}
70
+
71
+ // Destroy the interface
72
+ err = ioctl (uintptr (s ), syscall .SIOCIFDESTROY , uintptr (unsafe .Pointer (& ifreq )))
73
+ return err
37
74
}
75
+
38
76
return nil
39
77
}
40
78
@@ -43,34 +81,87 @@ func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []Route, _ int
43
81
}
44
82
45
83
func newTun (l * logrus.Logger , deviceName string , cidr * net.IPNet , defaultMTU int , routes []Route , _ int , _ bool , _ bool ) (* tun , error ) {
46
- routeTree , err := makeRouteTree (l , routes , false )
84
+ // Try to open existing tun device
85
+ var file * os.File
86
+ var err error
87
+ if deviceName != "" {
88
+ file , err = os .OpenFile ("/dev/" + deviceName , os .O_RDWR , 0 )
89
+ }
90
+ if errors .Is (err , fs .ErrNotExist ) || deviceName == "" {
91
+ // If the device doesn't already exist, request a new one and rename it
92
+ file , err = os .OpenFile ("/dev/tun" , os .O_RDWR , 0 )
93
+ }
94
+ if err != nil {
95
+ return nil , err
96
+ }
97
+
98
+ rawConn , err := file .SyscallConn ()
47
99
if err != nil {
100
+ return nil , fmt .Errorf ("SyscallConn: %v" , err )
101
+ }
102
+
103
+ var name [16 ]byte
104
+ var ctrlErr error
105
+ rawConn .Control (func (fd uintptr ) {
106
+ // Read the name of the interface
107
+ arg := fiodgnameArg {length : 16 , buf : unsafe .Pointer (& name )}
108
+ ctrlErr = ioctl (fd , FIODGNAME , uintptr (unsafe .Pointer (& arg )))
109
+ })
110
+ if ctrlErr != nil {
48
111
return nil , err
49
112
}
50
113
51
- if strings .HasPrefix (deviceName , "/dev/" ) {
52
- deviceName = strings .TrimPrefix (deviceName , "/dev/" )
114
+ ifName := string (bytes .TrimRight (name [:], "\x00 " ))
115
+ if deviceName == "" {
116
+ deviceName = ifName
53
117
}
54
- if ! deviceNameRE .MatchString (deviceName ) {
55
- return nil , fmt .Errorf ("tun.dev must match `tun[0-9]+`" )
118
+
119
+ // If the name doesn't match the desired interface name, rename it now
120
+ if ifName != deviceName {
121
+ s , err := syscall .Socket (
122
+ syscall .AF_INET ,
123
+ syscall .SOCK_DGRAM ,
124
+ syscall .IPPROTO_IP ,
125
+ )
126
+ if err != nil {
127
+ return nil , err
128
+ }
129
+ defer syscall .Close (s )
130
+
131
+ fd := uintptr (s )
132
+
133
+ var fromName [16 ]byte
134
+ var toName [16 ]byte
135
+ copy (fromName [:], ifName )
136
+ copy (toName [:], deviceName )
137
+
138
+ ifrr := ifreqRename {
139
+ Name : fromName ,
140
+ Data : uintptr (unsafe .Pointer (& toName )),
141
+ }
142
+
143
+ // Set the device name
144
+ ioctl (fd , syscall .SIOCSIFNAME , uintptr (unsafe .Pointer (& ifrr )))
56
145
}
146
+
147
+ routeTree , err := makeRouteTree (l , routes , false )
148
+ if err != nil {
149
+ return nil , err
150
+ }
151
+
57
152
return & tun {
58
- Device : deviceName ,
59
- cidr : cidr ,
60
- MTU : defaultMTU ,
61
- Routes : routes ,
62
- routeTree : routeTree ,
63
- l : l ,
153
+ ReadWriteCloser : file ,
154
+ Device : deviceName ,
155
+ cidr : cidr ,
156
+ MTU : defaultMTU ,
157
+ Routes : routes ,
158
+ routeTree : routeTree ,
159
+ l : l ,
64
160
}, nil
65
161
}
66
162
67
163
func (t * tun ) Activate () error {
68
164
var err error
69
- t .ReadWriteCloser , err = os .OpenFile ("/dev/" + t .Device , os .O_RDWR , 0 )
70
- if err != nil {
71
- return fmt .Errorf ("activate failed: %v" , err )
72
- }
73
-
74
165
// TODO use syscalls instead of exec.Command
75
166
t .l .Debug ("command: ifconfig" , t .Device , t .cidr .String (), t .cidr .IP .String ())
76
167
if err = exec .Command ("/sbin/ifconfig" , t .Device , t .cidr .String (), t .cidr .IP .String ()).Run (); err != nil {
@@ -120,3 +211,10 @@ func (t *tun) Name() string {
120
211
func (t * tun ) NewMultiQueueReader () (io.ReadWriteCloser , error ) {
121
212
return nil , fmt .Errorf ("TODO: multiqueue not implemented for freebsd" )
122
213
}
214
+
215
+ func (t * tun ) deviceBytes () (o [16 ]byte ) {
216
+ for i , c := range t .Device {
217
+ o [i ] = byte (c )
218
+ }
219
+ return
220
+ }
0 commit comments